diff --git a/.github/unityci/Unity_2018_Settings/Packages/manifest.json b/.github/unityci/Unity_2018_Settings/Packages/manifest.json new file mode 100644 index 00000000..0c11ed77 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/Packages/manifest.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "com.timcassell.protopromise": "file:../../Package", + "com.unity.package-manager-ui": "2.0.13", + "com.unity.textmeshpro": "1.4.1", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0" + }, + "testables": [ + "com.timcassell.protopromise" + ] +} diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/AudioManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/AudioManager.asset new file mode 100644 index 00000000..da611257 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 0 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/ClusterInputManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 00000000..e7886b26 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/DynamicsManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/DynamicsManager.asset new file mode 100644 index 00000000..78992f08 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorBuildSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 00000000..c148a638 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Demo/Demo.unity + guid: 5c627422c6d261241b596eecfb01db3c + m_configObjects: {} diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorSettings.asset new file mode 100644 index 00000000..7b426952 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/EditorSettings.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_ExternalVersionControlSupport: Hidden Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 0 + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/GraphicsSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 00000000..74d7b532 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/InputManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/InputManager.asset new file mode 100644 index 00000000..17c8f538 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/NavMeshAreas.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 00000000..3b0b7c3d --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/Physics2DSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 00000000..132ee6bc --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_ChangeStopsCallbacks: 0 + m_CallbacksOnDisable: 1 + m_AutoSyncTransforms: 1 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/PresetManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/PresetManager.asset new file mode 100644 index 00000000..636a595b --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/PresetManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + m_DefaultList: [] diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectSettings.asset new file mode 100644 index 00000000..6d69c72f --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,615 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 18 + productGUID: 91da7aa4063ecef42acb0206a8b3e580 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: ProtoPromise_Unity + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + displayResolutionDialog: 1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 0 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + vulkanEnableSetSRGBWrite: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 0 + xboxOneEnable7thCore: 0 + isWsaHolographicRemotingEnabled: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 0 + oculus: + sharedDepthBuffer: 0 + dashSupport: 0 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + protectGraphicsMemory: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 0 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 214 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 9.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 9.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPhone58inPortraitSplashScreen: {fileID: 0} + iPhone58inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + AndroidTargetArchitectures: 5 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WebGLSupport + m_APIs: 0b00000008000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + m_BuildTargetEnableVuforiaSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 3 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 0 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 2 + webGLLinkerTarget: 2 + webGLThreadsSupport: 0 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: + Standalone: 1 + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: ProtoPromise_Unity + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: ProtoPromise_Unity + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 0 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + xboxOneScriptCompiler: 0 + XboxOneOverrideIdentityName: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_PrivateKeyPath: + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + facebookSdkVersion: 7.9.4 + facebookAppId: + facebookCookies: 1 + facebookLogging: 1 + facebookStatus: 1 + facebookXfbml: 0 + facebookFrictionlessRequests: 1 + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 1 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectVersion.txt b/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectVersion.txt new file mode 100644 index 00000000..ff8802c3 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2018.4.36f1 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/QualitySettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/QualitySettings.asset new file mode 100644 index 00000000..05daac3c --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/QualitySettings.asset @@ -0,0 +1,191 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Standalone: 5 + Tizen: 2 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/TagManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/TagManager.asset new file mode 100644 index 00000000..1c92a784 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/TimeManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/TimeManager.asset new file mode 100644 index 00000000..558a017e --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/UnityConnectSettings.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 00000000..3da14d5b --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes + m_NativeEventUrl: https://perf-events.cloud.unity3d.com/symbolicate + m_Enabled: 0 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/.github/unityci/Unity_2018_Settings/ProjectSettings/VFXManager.asset b/.github/unityci/Unity_2018_Settings/ProjectSettings/VFXManager.asset new file mode 100644 index 00000000..6e0eaca4 --- /dev/null +++ b/.github/unityci/Unity_2018_Settings/ProjectSettings/VFXManager.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/.github/unityci/Unity_2019_Settings/Packages/manifest.json b/.github/unityci/Unity_2019_Settings/Packages/manifest.json new file mode 100644 index 00000000..7bdf4753 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/Packages/manifest.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "com.timcassell.protopromise": "file:../../Package", + "com.unity.test-framework": "1.1.31", + "com.unity.textmeshpro": "2.1.4", + "com.unity.ugui": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0" + }, + "testables": [ + "com.timcassell.protopromise" + ] +} diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/AudioManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/AudioManager.asset new file mode 100644 index 00000000..da611257 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 0 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/ClusterInputManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 00000000..e7886b26 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/DynamicsManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/DynamicsManager.asset new file mode 100644 index 00000000..78992f08 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorBuildSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 00000000..c148a638 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Demo/Demo.unity + guid: 5c627422c6d261241b596eecfb01db3c + m_configObjects: {} diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorSettings.asset new file mode 100644 index 00000000..b30a347e --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/EditorSettings.asset @@ -0,0 +1,36 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 9 + m_ExternalVersionControlSupport: Hidden Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 0 + m_PrefabRegularEnvironment: {fileID: 0} + m_PrefabUIEnvironment: {fileID: 0} + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp;asmref + m_ProjectGenerationRootNamespace: + m_CollabEditorSettings: + inProgressEnabled: 1 + m_EnableTextureStreamingInEditMode: 1 + m_EnableTextureStreamingInPlayMode: 1 + m_AsyncShaderCompilation: 1 + m_EnterPlayModeOptionsEnabled: 0 + m_EnterPlayModeOptions: 3 + m_ShowLightmapResolutionOverlay: 1 + m_UseLegacyProbeSampleCount: 1 + m_AssetPipelineMode: 1 + m_CacheServerMode: 2 + m_CacheServerEndpoint: + m_CacheServerNamespacePrefix: default + m_CacheServerEnableDownload: 1 + m_CacheServerEnableUpload: 1 + m_CacheServerValidationMode: 2 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/GraphicsSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 00000000..e04e8483 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,66 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 13 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16000, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 16001, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 17000, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 + m_LogWhenShaderIsCompiled: 0 + m_AllowEnlightenSupportForUpgradedProject: 1 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/InputManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/InputManager.asset new file mode 100644 index 00000000..17c8f538 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/NavMeshAreas.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 00000000..3b0b7c3d --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/PackageManagerSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 00000000..6920e3ad --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,38 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_ScopedRegistriesSettingsExpanded: 1 + oneTimeWarningShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_ErrorMessage: + m_Original: + m_Id: + m_Name: + m_Url: + m_Scopes: [] + m_IsDefault: 0 + m_Modified: 0 + m_Name: + m_Url: + m_Scopes: + - + m_SelectedScopeIndex: 0 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/Physics2DSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 00000000..132ee6bc --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_ChangeStopsCallbacks: 0 + m_CallbacksOnDisable: 1 + m_AutoSyncTransforms: 1 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/PresetManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/PresetManager.asset new file mode 100644 index 00000000..636a595b --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/PresetManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + m_DefaultList: [] diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectSettings.asset new file mode 100644 index 00000000..0b04810e --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,694 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 20 + productGUID: 91da7aa4063ecef42acb0206a8b3e580 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: ProtoPromise_Unity + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 0 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 0 + androidUseSwappy: 0 + androidBlitType: 0 + androidResizableWindow: 0 + androidDefaultWindowWidth: 1920 + androidDefaultWindowHeight: 1080 + androidMinimumWindowWidth: 400 + androidMinimumWindowHeight: 300 + androidFullscreenMode: 1 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + useFlipModelSwapchain: 1 + resizableWindow: 1 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + fullscreenMode: 3 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + stadiaPresentMode: 0 + stadiaTargetFramerate: 0 + vulkanNumSwapchainBuffers: 3 + vulkanEnableSetSRGBWrite: 0 + vulkanEnableLateAcquireNextImage: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 0 + xboxOneEnable7thCore: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 0 + lumin: + depthFormat: 0 + frameTiming: 2 + enableGLCache: 0 + glCacheMaxBlobSize: 524288 + glCacheMaxFileSize: 8388608 + oculus: + sharedDepthBuffer: 0 + dashSupport: 0 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + isWsaHolographicRemotingEnabled: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + D3DHDRBitDepth: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 19 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: nimt-trampolines=1024 + stripEngineCode: 0 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 214 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 10.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 10.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + AndroidTargetArchitectures: 5 + AndroidTargetDevices: 0 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + androidUseCustomKeystore: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + chromeosInputEmulation: 1 + AndroidValidateAppBundleSize: 1 + AndroidAppBundleSizeToValidate: 150 + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsJobs: + - m_BuildTarget: MacStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: Switch + m_GraphicsJobs: 0 + - m_BuildTarget: MetroSupport + m_GraphicsJobs: 0 + - m_BuildTarget: GameCoreScarlettSupport + m_GraphicsJobs: 0 + - m_BuildTarget: AppleTVSupport + m_GraphicsJobs: 0 + - m_BuildTarget: BJMSupport + m_GraphicsJobs: 0 + - m_BuildTarget: LinuxStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: GameCoreXboxOneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS4Player + m_GraphicsJobs: 0 + - m_BuildTarget: iOSSupport + m_GraphicsJobs: 0 + - m_BuildTarget: PS5Player + m_GraphicsJobs: 0 + - m_BuildTarget: WindowsStandaloneSupport + m_GraphicsJobs: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobs: 0 + - m_BuildTarget: LuminSupport + m_GraphicsJobs: 0 + - m_BuildTarget: CloudRendering + m_GraphicsJobs: 0 + - m_BuildTarget: AndroidPlayer + m_GraphicsJobs: 0 + - m_BuildTarget: WebGLSupport + m_GraphicsJobs: 0 + m_BuildTargetGraphicsJobMode: + - m_BuildTarget: PS4Player + m_GraphicsJobMode: 0 + - m_BuildTarget: XboxOnePlayer + m_GraphicsJobMode: 0 + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WebGLSupport + m_APIs: 0b00000008000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + openGLRequireES32: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 3 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + switchUseMicroSleepForYield: 1 + switchEnableRamDiskSupport: 0 + switchMicroSleepForYieldTime: 25 + switchRamDiskSpaceSize: 12 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + playerPrefsMaxSize: 32768 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4UseLowGarlicFragmentationMode: 1 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + ps4attribVROutputEnabled: 0 + ps5ParamFilePath: + ps5VideoOutPixelFormat: 0 + ps5VideoOutInitialWidth: 1920 + ps5VideoOutOutputMode: 1 + ps5BackgroundImagePath: + ps5StartupImagePath: + ps5Pic2Path: + ps5StartupImagesFolder: + ps5IconImagesFolder: + ps5SaveDataImagePath: + ps5SdkOverride: + ps5BGMPath: + ps5ShareOverlayImagePath: + ps5NPConfigZipPath: + ps5Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps5UseResolutionFallback: 0 + ps5UseAudio3dBackend: 0 + ps5ScriptOptimizationLevel: 2 + ps5Audio3dVirtualSpeakerCount: 14 + ps5VrrSupport: 0 + ps5UpdateReferencePackage: + ps5disableAutoHideSplash: 0 + ps5OperatingSystemCanDisableSplashScreen: 0 + ps5IncludedModules: [] + ps5SharedBinaryContentLabels: [] + ps5SharedBinarySystemFolders: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + blurSplashScreenBackground: 1 + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 0 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 2 + webGLLinkerTarget: 2 + webGLThreadsSupport: 0 + scriptingDefineSymbols: {} + webGLWasmStreaming: 0 + platformArchitecture: {} + scriptingBackend: + Standalone: 1 + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + suppressCommonWarnings: 1 + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + gcIncremental: 0 + assemblyVersionValidation: 1 + gcWBarrierValidation: 0 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: ProtoPromise_Unity + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: ProtoPromise_Unity + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnhancedXboxCompatibilityMode: 0 + XboxOneEnableGPUVariability: 0 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + XboxOneOverrideIdentityName: + XboxOneOverrideIdentityPublisher: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_SignPackage: 1 + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 1 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectVersion.txt b/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectVersion.txt new file mode 100644 index 00000000..4c191298 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2019.4.40f1 +m_EditorVersionWithRevision: 2019.4.40f1 (ffc62b691db5) diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/QualitySettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/QualitySettings.asset new file mode 100644 index 00000000..05daac3c --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/QualitySettings.asset @@ -0,0 +1,191 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Standalone: 5 + Tizen: 2 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/TagManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/TagManager.asset new file mode 100644 index 00000000..1c92a784 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/TimeManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/TimeManager.asset new file mode 100644 index 00000000..558a017e --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/UnityConnectSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 00000000..3da14d5b --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes + m_NativeEventUrl: https://perf-events.cloud.unity3d.com/symbolicate + m_Enabled: 0 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/VFXManager.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/VFXManager.asset new file mode 100644 index 00000000..6e0eaca4 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/VFXManager.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/.github/unityci/Unity_2019_Settings/ProjectSettings/XRSettings.asset b/.github/unityci/Unity_2019_Settings/ProjectSettings/XRSettings.asset new file mode 100644 index 00000000..482590c1 --- /dev/null +++ b/.github/unityci/Unity_2019_Settings/ProjectSettings/XRSettings.asset @@ -0,0 +1,10 @@ +{ + "m_SettingKeys": [ + "VR Device Disabled", + "VR Device User Alert" + ], + "m_SettingValues": [ + "False", + "False" + ] +} \ No newline at end of file diff --git a/.github/unityci/Unity_2023_Settings/Packages/manifest.json b/.github/unityci/Unity_2023_Settings/Packages/manifest.json new file mode 100644 index 00000000..d1cb2f85 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/Packages/manifest.json @@ -0,0 +1,16 @@ +{ + "dependencies": { + "com.timcassell.protopromise": "file:../../Package", + "com.unity.test-framework": "1.3.5", + "com.unity.textmeshpro": "3.0.6", + "com.unity.ugui": "1.0.0", + "com.unity.modules.imageconversion": "1.0.0", + "com.unity.modules.imgui": "1.0.0", + "com.unity.modules.ui": "1.0.0", + "com.unity.modules.unitywebrequest": "1.0.0", + "com.unity.modules.unitywebrequesttexture": "1.0.0" + }, + "testables": [ + "com.timcassell.protopromise" + ] +} diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/AudioManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/AudioManager.asset new file mode 100644 index 00000000..da611257 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/AudioManager.asset @@ -0,0 +1,17 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!11 &1 +AudioManager: + m_ObjectHideFlags: 0 + m_Volume: 1 + Rolloff Scale: 1 + Doppler Factor: 1 + Default Speaker Mode: 2 + m_SampleRate: 0 + m_DSPBufferSize: 0 + m_VirtualVoiceCount: 512 + m_RealVoiceCount: 32 + m_SpatializerPlugin: + m_AmbisonicDecoderPlugin: + m_DisableAudio: 0 + m_VirtualizeEffects: 1 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/ClusterInputManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 00000000..e7886b26 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/ClusterInputManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!236 &1 +ClusterInputManager: + m_ObjectHideFlags: 0 + m_Inputs: [] diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/DynamicsManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/DynamicsManager.asset new file mode 100644 index 00000000..78992f08 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/DynamicsManager.asset @@ -0,0 +1,29 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!55 &1 +PhysicsManager: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_Gravity: {x: 0, y: -9.81, z: 0} + m_DefaultMaterial: {fileID: 0} + m_BounceThreshold: 2 + m_SleepThreshold: 0.005 + m_DefaultContactOffset: 0.01 + m_DefaultSolverIterations: 6 + m_DefaultSolverVelocityIterations: 1 + m_QueriesHitBackfaces: 0 + m_QueriesHitTriggers: 1 + m_EnableAdaptiveForce: 0 + m_ClothInterCollisionDistance: 0 + m_ClothInterCollisionStiffness: 0 + m_ContactsGeneration: 1 + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff + m_AutoSimulation: 1 + m_AutoSyncTransforms: 1 + m_ClothInterCollisionSettingsToggle: 0 + m_ContactPairsMode: 0 + m_BroadphaseType: 0 + m_WorldBounds: + m_Center: {x: 0, y: 0, z: 0} + m_Extent: {x: 250, y: 250, z: 250} + m_WorldSubdivisions: 8 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorBuildSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorBuildSettings.asset new file mode 100644 index 00000000..c148a638 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorBuildSettings.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1045 &1 +EditorBuildSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Scenes: + - enabled: 1 + path: Assets/Demo/Demo.unity + guid: 5c627422c6d261241b596eecfb01db3c + m_configObjects: {} diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorSettings.asset new file mode 100644 index 00000000..7b426952 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/EditorSettings.asset @@ -0,0 +1,21 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!159 &1 +EditorSettings: + m_ObjectHideFlags: 0 + serializedVersion: 7 + m_ExternalVersionControlSupport: Hidden Meta Files + m_SerializationMode: 2 + m_LineEndingsForNewScripts: 2 + m_DefaultBehaviorMode: 0 + m_SpritePackerMode: 0 + m_SpritePackerPaddingPower: 1 + m_EtcTextureCompressorBehavior: 1 + m_EtcTextureFastCompressor: 1 + m_EtcTextureNormalCompressor: 2 + m_EtcTextureBestCompressor: 4 + m_ProjectGenerationIncludedExtensions: txt;xml;fnt;cd;asmdef;rsp + m_ProjectGenerationRootNamespace: + m_UserGeneratedProjectSuffix: + m_CollabEditorSettings: + inProgressEnabled: 1 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/GraphicsSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 00000000..74d7b532 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/GraphicsSettings.asset @@ -0,0 +1,61 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!30 &1 +GraphicsSettings: + m_ObjectHideFlags: 0 + serializedVersion: 12 + m_Deferred: + m_Mode: 1 + m_Shader: {fileID: 69, guid: 0000000000000000f000000000000000, type: 0} + m_DeferredReflections: + m_Mode: 1 + m_Shader: {fileID: 74, guid: 0000000000000000f000000000000000, type: 0} + m_ScreenSpaceShadows: + m_Mode: 1 + m_Shader: {fileID: 64, guid: 0000000000000000f000000000000000, type: 0} + m_LegacyDeferred: + m_Mode: 1 + m_Shader: {fileID: 63, guid: 0000000000000000f000000000000000, type: 0} + m_DepthNormals: + m_Mode: 1 + m_Shader: {fileID: 62, guid: 0000000000000000f000000000000000, type: 0} + m_MotionVectors: + m_Mode: 1 + m_Shader: {fileID: 75, guid: 0000000000000000f000000000000000, type: 0} + m_LightHalo: + m_Mode: 1 + m_Shader: {fileID: 105, guid: 0000000000000000f000000000000000, type: 0} + m_LensFlare: + m_Mode: 1 + m_Shader: {fileID: 102, guid: 0000000000000000f000000000000000, type: 0} + m_AlwaysIncludedShaders: + - {fileID: 7, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15104, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15105, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 15106, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10753, guid: 0000000000000000f000000000000000, type: 0} + - {fileID: 10770, guid: 0000000000000000f000000000000000, type: 0} + m_PreloadedShaders: [] + m_SpritesDefaultMaterial: {fileID: 10754, guid: 0000000000000000f000000000000000, + type: 0} + m_CustomRenderPipeline: {fileID: 0} + m_TransparencySortMode: 0 + m_TransparencySortAxis: {x: 0, y: 0, z: 1} + m_DefaultRenderingPath: 1 + m_DefaultMobileRenderingPath: 1 + m_TierSettings: [] + m_LightmapStripping: 0 + m_FogStripping: 0 + m_InstancingStripping: 0 + m_LightmapKeepPlain: 1 + m_LightmapKeepDirCombined: 1 + m_LightmapKeepDynamicPlain: 1 + m_LightmapKeepDynamicDirCombined: 1 + m_LightmapKeepShadowMask: 1 + m_LightmapKeepSubtractive: 1 + m_FogKeepLinear: 1 + m_FogKeepExp: 1 + m_FogKeepExp2: 1 + m_AlbedoSwatchInfos: [] + m_LightsUseLinearIntensity: 0 + m_LightsUseColorTemperature: 0 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/InputManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/InputManager.asset new file mode 100644 index 00000000..17c8f538 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/InputManager.asset @@ -0,0 +1,295 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!13 &1 +InputManager: + m_ObjectHideFlags: 0 + serializedVersion: 2 + m_Axes: + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: left + positiveButton: right + altNegativeButton: a + altPositiveButton: d + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: down + positiveButton: up + altNegativeButton: s + altPositiveButton: w + gravity: 3 + dead: 0.001 + sensitivity: 3 + snap: 1 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left ctrl + altNegativeButton: + altPositiveButton: mouse 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left alt + altNegativeButton: + altPositiveButton: mouse 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: left shift + altNegativeButton: + altPositiveButton: mouse 2 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: space + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse X + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse Y + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Mouse ScrollWheel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0 + sensitivity: 0.1 + snap: 0 + invert: 0 + type: 1 + axis: 2 + joyNum: 0 + - serializedVersion: 3 + m_Name: Horizontal + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 0 + type: 2 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Vertical + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: + altNegativeButton: + altPositiveButton: + gravity: 0 + dead: 0.19 + sensitivity: 1 + snap: 0 + invert: 1 + type: 2 + axis: 1 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire1 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 0 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire2 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 1 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Fire3 + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 2 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Jump + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: joystick button 3 + altNegativeButton: + altPositiveButton: + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: return + altNegativeButton: + altPositiveButton: joystick button 0 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Submit + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: enter + altNegativeButton: + altPositiveButton: space + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 + - serializedVersion: 3 + m_Name: Cancel + descriptiveName: + descriptiveNegativeName: + negativeButton: + positiveButton: escape + altNegativeButton: + altPositiveButton: joystick button 1 + gravity: 1000 + dead: 0.001 + sensitivity: 1000 + snap: 0 + invert: 0 + type: 0 + axis: 0 + joyNum: 0 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/MemorySettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/MemorySettings.asset new file mode 100644 index 00000000..5b5facec --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/MemorySettings.asset @@ -0,0 +1,35 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!387306366 &1 +MemorySettings: + m_ObjectHideFlags: 0 + m_EditorMemorySettings: + m_MainAllocatorBlockSize: -1 + m_ThreadAllocatorBlockSize: -1 + m_MainGfxBlockSize: -1 + m_ThreadGfxBlockSize: -1 + m_CacheBlockSize: -1 + m_TypetreeBlockSize: -1 + m_ProfilerBlockSize: -1 + m_ProfilerEditorBlockSize: -1 + m_BucketAllocatorGranularity: -1 + m_BucketAllocatorBucketsCount: -1 + m_BucketAllocatorBlockSize: -1 + m_BucketAllocatorBlockCount: -1 + m_ProfilerBucketAllocatorGranularity: -1 + m_ProfilerBucketAllocatorBucketsCount: -1 + m_ProfilerBucketAllocatorBlockSize: -1 + m_ProfilerBucketAllocatorBlockCount: -1 + m_TempAllocatorSizeMain: -1 + m_JobTempAllocatorBlockSize: -1 + m_BackgroundJobTempAllocatorBlockSize: -1 + m_JobTempAllocatorReducedBlockSize: -1 + m_TempAllocatorSizeGIBakingWorker: -1 + m_TempAllocatorSizeNavMeshWorker: -1 + m_TempAllocatorSizeAudioWorker: -1 + m_TempAllocatorSizeCloudWorker: -1 + m_TempAllocatorSizeGfx: -1 + m_TempAllocatorSizeJobWorker: -1 + m_TempAllocatorSizeBackgroundWorker: -1 + m_TempAllocatorSizePreloadManager: -1 + m_PlatformMemorySettings: {} diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/NavMeshAreas.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 00000000..3b0b7c3d --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/NavMeshAreas.asset @@ -0,0 +1,91 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!126 &1 +NavMeshProjectSettings: + m_ObjectHideFlags: 0 + serializedVersion: 2 + areas: + - name: Walkable + cost: 1 + - name: Not Walkable + cost: 1 + - name: Jump + cost: 2 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + - name: + cost: 1 + m_LastAgentTypeID: -887442657 + m_Settings: + - serializedVersion: 2 + agentTypeID: 0 + agentRadius: 0.5 + agentHeight: 2 + agentSlope: 45 + agentClimb: 0.75 + ledgeDropHeight: 0 + maxJumpAcrossDistance: 0 + minRegionArea: 2 + manualCellSize: 0 + cellSize: 0.16666667 + manualTileSize: 0 + tileSize: 256 + accuratePlacement: 0 + debug: + m_Flags: 0 + m_SettingNames: + - Humanoid diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/PackageManagerSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/PackageManagerSettings.asset new file mode 100644 index 00000000..7de173aa --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/PackageManagerSettings.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!114 &1 +MonoBehaviour: + m_ObjectHideFlags: 61 + m_CorrespondingSourceObject: {fileID: 0} + m_PrefabInstance: {fileID: 0} + m_PrefabAsset: {fileID: 0} + m_GameObject: {fileID: 0} + m_Enabled: 1 + m_EditorHideFlags: 0 + m_Script: {fileID: 13964, guid: 0000000000000000e000000000000000, type: 0} + m_Name: + m_EditorClassIdentifier: + m_EnablePreReleasePackages: 0 + m_AdvancedSettingsExpanded: 1 + m_ScopedRegistriesSettingsExpanded: 1 + m_SeeAllPackageVersions: 0 + m_DismissPreviewPackagesInUse: 0 + oneTimeWarningShown: 0 + oneTimeDeprecatedPopUpShown: 0 + m_Registries: + - m_Id: main + m_Name: + m_Url: https://packages.unity.com + m_Scopes: [] + m_IsDefault: 1 + m_Capabilities: 7 + m_ConfigSource: 0 + m_UserSelectedRegistryName: + m_UserAddingNewScopedRegistry: 0 + m_RegistryInfoDraft: + m_Modified: 0 + m_ErrorMessage: + m_UserModificationsInstanceId: -866 + m_OriginalInstanceId: -868 + m_LoadAssets: 0 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/Physics2DSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 00000000..132ee6bc --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/Physics2DSettings.asset @@ -0,0 +1,37 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!19 &1 +Physics2DSettings: + m_ObjectHideFlags: 0 + serializedVersion: 3 + m_Gravity: {x: 0, y: -9.81} + m_DefaultMaterial: {fileID: 0} + m_VelocityIterations: 8 + m_PositionIterations: 3 + m_VelocityThreshold: 1 + m_MaxLinearCorrection: 0.2 + m_MaxAngularCorrection: 8 + m_MaxTranslationSpeed: 100 + m_MaxRotationSpeed: 360 + m_BaumgarteScale: 0.2 + m_BaumgarteTimeOfImpactScale: 0.75 + m_TimeToSleep: 0.5 + m_LinearSleepTolerance: 0.01 + m_AngularSleepTolerance: 2 + m_DefaultContactOffset: 0.01 + m_AutoSimulation: 1 + m_QueriesHitTriggers: 1 + m_QueriesStartInColliders: 1 + m_ChangeStopsCallbacks: 0 + m_CallbacksOnDisable: 1 + m_AutoSyncTransforms: 1 + m_AlwaysShowColliders: 0 + m_ShowColliderSleep: 1 + m_ShowColliderContacts: 0 + m_ShowColliderAABB: 0 + m_ContactArrowScale: 0.2 + m_ColliderAwakeColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.7529412} + m_ColliderAsleepColor: {r: 0.5686275, g: 0.95686275, b: 0.54509807, a: 0.36078432} + m_ColliderContactColor: {r: 1, g: 0, b: 1, a: 0.6862745} + m_ColliderAABBColor: {r: 1, g: 1, b: 0, a: 0.2509804} + m_LayerCollisionMatrix: ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/PresetManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/PresetManager.asset new file mode 100644 index 00000000..636a595b --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/PresetManager.asset @@ -0,0 +1,6 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!1386491679 &1 +PresetManager: + m_ObjectHideFlags: 0 + m_DefaultList: [] diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectSettings.asset new file mode 100644 index 00000000..6d69c72f --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectSettings.asset @@ -0,0 +1,615 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!129 &1 +PlayerSettings: + m_ObjectHideFlags: 0 + serializedVersion: 18 + productGUID: 91da7aa4063ecef42acb0206a8b3e580 + AndroidProfiler: 0 + AndroidFilterTouchesWhenObscured: 0 + AndroidEnableSustainedPerformanceMode: 0 + defaultScreenOrientation: 4 + targetDevice: 2 + useOnDemandResources: 0 + accelerometerFrequency: 60 + companyName: DefaultCompany + productName: ProtoPromise_Unity + defaultCursor: {fileID: 0} + cursorHotspot: {x: 0, y: 0} + m_SplashScreenBackgroundColor: {r: 0.13725491, g: 0.12156863, b: 0.1254902, a: 1} + m_ShowUnitySplashScreen: 1 + m_ShowUnitySplashLogo: 1 + m_SplashScreenOverlayOpacity: 1 + m_SplashScreenAnimation: 1 + m_SplashScreenLogoStyle: 1 + m_SplashScreenDrawMode: 0 + m_SplashScreenBackgroundAnimationZoom: 1 + m_SplashScreenLogoAnimationZoom: 1 + m_SplashScreenBackgroundLandscapeAspect: 1 + m_SplashScreenBackgroundPortraitAspect: 1 + m_SplashScreenBackgroundLandscapeUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenBackgroundPortraitUvs: + serializedVersion: 2 + x: 0 + y: 0 + width: 1 + height: 1 + m_SplashScreenLogos: [] + m_VirtualRealitySplashScreen: {fileID: 0} + m_HolographicTrackingLossScreen: {fileID: 0} + defaultScreenWidth: 1024 + defaultScreenHeight: 768 + defaultScreenWidthWeb: 960 + defaultScreenHeightWeb: 600 + m_StereoRenderingPath: 0 + m_ActiveColorSpace: 0 + m_MTRendering: 1 + m_StackTraceTypes: 010000000100000001000000010000000100000001000000 + iosShowActivityIndicatorOnLoading: -1 + androidShowActivityIndicatorOnLoading: -1 + displayResolutionDialog: 1 + iosUseCustomAppBackgroundBehavior: 0 + iosAllowHTTPDownload: 1 + allowedAutorotateToPortrait: 1 + allowedAutorotateToPortraitUpsideDown: 1 + allowedAutorotateToLandscapeRight: 1 + allowedAutorotateToLandscapeLeft: 1 + useOSAutorotation: 1 + use32BitDisplayBuffer: 1 + preserveFramebufferAlpha: 0 + disableDepthAndStencilBuffers: 0 + androidStartInFullscreen: 1 + androidRenderOutsideSafeArea: 0 + androidBlitType: 0 + defaultIsNativeResolution: 1 + macRetinaSupport: 1 + runInBackground: 1 + captureSingleScreen: 0 + muteOtherAudioSources: 0 + Prepare IOS For Recording: 0 + Force IOS Speakers When Recording: 0 + deferSystemGesturesMode: 0 + hideHomeButton: 0 + submitAnalytics: 1 + usePlayerLog: 1 + bakeCollisionMeshes: 0 + forceSingleInstance: 0 + resizableWindow: 0 + useMacAppStoreValidation: 0 + macAppStoreCategory: public.app-category.games + gpuSkinning: 0 + graphicsJobs: 0 + xboxPIXTextureCapture: 0 + xboxEnableAvatar: 0 + xboxEnableKinect: 0 + xboxEnableKinectAutoTracking: 0 + xboxEnableFitness: 0 + visibleInBackground: 1 + allowFullscreenSwitch: 1 + graphicsJobMode: 0 + fullscreenMode: 1 + xboxSpeechDB: 0 + xboxEnableHeadOrientation: 0 + xboxEnableGuest: 0 + xboxEnablePIXSampling: 0 + metalFramebufferOnly: 0 + xboxOneResolution: 0 + xboxOneSResolution: 0 + xboxOneXResolution: 3 + xboxOneMonoLoggingLevel: 0 + xboxOneLoggingLevel: 1 + xboxOneDisableEsram: 0 + xboxOneEnableTypeOptimization: 0 + xboxOnePresentImmediateThreshold: 0 + switchQueueCommandMemory: 1048576 + switchQueueControlMemory: 16384 + switchQueueComputeMemory: 262144 + switchNVNShaderPoolsGranularity: 33554432 + switchNVNDefaultPoolsGranularity: 16777216 + switchNVNOtherPoolsGranularity: 16777216 + switchNVNMaxPublicTextureIDCount: 0 + switchNVNMaxPublicSamplerIDCount: 0 + vulkanEnableSetSRGBWrite: 0 + m_SupportedAspectRatios: + 4:3: 1 + 5:4: 1 + 16:10: 1 + 16:9: 1 + Others: 1 + bundleVersion: 1.0 + preloadedAssets: [] + metroInputSource: 0 + wsaTransparentSwapchain: 0 + m_HolographicPauseOnTrackingLoss: 1 + xboxOneDisableKinectGpuReservation: 0 + xboxOneEnable7thCore: 0 + isWsaHolographicRemotingEnabled: 0 + vrSettings: + cardboard: + depthFormat: 0 + enableTransitionView: 0 + daydream: + depthFormat: 0 + useSustainedPerformanceMode: 0 + enableVideoLayer: 0 + useProtectedVideoMemory: 0 + minimumSupportedHeadTracking: 0 + maximumSupportedHeadTracking: 1 + hololens: + depthFormat: 1 + depthBufferSharingEnabled: 0 + oculus: + sharedDepthBuffer: 0 + dashSupport: 0 + lowOverheadMode: 0 + protectedContext: 0 + v2Signing: 0 + enable360StereoCapture: 0 + protectGraphicsMemory: 0 + enableFrameTimingStats: 0 + useHDRDisplay: 0 + m_ColorGamuts: 00000000 + targetPixelDensity: 30 + resolutionScalingMode: 0 + androidSupportedAspectRatio: 1 + androidMaxAspectRatio: 2.1 + applicationIdentifier: {} + buildNumber: {} + AndroidBundleVersionCode: 1 + AndroidMinSdkVersion: 16 + AndroidTargetSdkVersion: 0 + AndroidPreferredInstallLocation: 1 + aotOptions: + stripEngineCode: 0 + iPhoneStrippingLevel: 0 + iPhoneScriptCallOptimization: 0 + ForceInternetPermission: 0 + ForceSDCardPermission: 0 + CreateWallpaper: 0 + APKExpansionFiles: 0 + keepLoadedShadersAlive: 0 + StripUnusedMeshComponents: 0 + VertexChannelCompressionMask: 214 + iPhoneSdkVersion: 988 + iOSTargetOSVersionString: 9.0 + tvOSSdkVersion: 0 + tvOSRequireExtendedGameController: 0 + tvOSTargetOSVersionString: 9.0 + uIPrerenderedIcon: 0 + uIRequiresPersistentWiFi: 0 + uIRequiresFullScreen: 1 + uIStatusBarHidden: 1 + uIExitOnSuspend: 0 + uIStatusBarStyle: 0 + iPhoneSplashScreen: {fileID: 0} + iPhoneHighResSplashScreen: {fileID: 0} + iPhoneTallHighResSplashScreen: {fileID: 0} + iPhone47inSplashScreen: {fileID: 0} + iPhone55inPortraitSplashScreen: {fileID: 0} + iPhone55inLandscapeSplashScreen: {fileID: 0} + iPhone58inPortraitSplashScreen: {fileID: 0} + iPhone58inLandscapeSplashScreen: {fileID: 0} + iPadPortraitSplashScreen: {fileID: 0} + iPadHighResPortraitSplashScreen: {fileID: 0} + iPadLandscapeSplashScreen: {fileID: 0} + iPadHighResLandscapeSplashScreen: {fileID: 0} + appleTVSplashScreen: {fileID: 0} + appleTVSplashScreen2x: {fileID: 0} + tvOSSmallIconLayers: [] + tvOSSmallIconLayers2x: [] + tvOSLargeIconLayers: [] + tvOSLargeIconLayers2x: [] + tvOSTopShelfImageLayers: [] + tvOSTopShelfImageLayers2x: [] + tvOSTopShelfImageWideLayers: [] + tvOSTopShelfImageWideLayers2x: [] + iOSLaunchScreenType: 0 + iOSLaunchScreenPortrait: {fileID: 0} + iOSLaunchScreenLandscape: {fileID: 0} + iOSLaunchScreenBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreenFillPct: 100 + iOSLaunchScreenSize: 100 + iOSLaunchScreenCustomXibPath: + iOSLaunchScreeniPadType: 0 + iOSLaunchScreeniPadImage: {fileID: 0} + iOSLaunchScreeniPadBackgroundColor: + serializedVersion: 2 + rgba: 0 + iOSLaunchScreeniPadFillPct: 100 + iOSLaunchScreeniPadSize: 100 + iOSLaunchScreeniPadCustomXibPath: + iOSUseLaunchScreenStoryboard: 0 + iOSLaunchScreenCustomStoryboardPath: + iOSDeviceRequirements: [] + iOSURLSchemes: [] + iOSBackgroundModes: 0 + iOSMetalForceHardShadows: 0 + metalEditorSupport: 1 + metalAPIValidation: 1 + iOSRenderExtraFrameOnPause: 0 + iosCopyPluginsCodeInsteadOfSymlink: 0 + appleDeveloperTeamID: + iOSManualSigningProvisioningProfileID: + tvOSManualSigningProvisioningProfileID: + iOSManualSigningProvisioningProfileType: 0 + tvOSManualSigningProvisioningProfileType: 0 + appleEnableAutomaticSigning: 0 + iOSRequireARKit: 0 + iOSAutomaticallyDetectAndAddCapabilities: 1 + appleEnableProMotion: 0 + clonedFromGUID: 00000000000000000000000000000000 + templatePackageId: + templateDefaultScene: + AndroidTargetArchitectures: 5 + AndroidSplashScreenScale: 0 + androidSplashScreen: {fileID: 0} + AndroidKeystoreName: + AndroidKeyaliasName: + AndroidBuildApkPerCpuArchitecture: 0 + AndroidTVCompatibility: 1 + AndroidIsGame: 1 + AndroidEnableTango: 0 + androidEnableBanner: 1 + androidUseLowAccuracyLocation: 0 + m_AndroidBanners: + - width: 320 + height: 180 + banner: {fileID: 0} + androidGamepadSupportLevel: 0 + resolutionDialogBanner: {fileID: 0} + m_BuildTargetIcons: [] + m_BuildTargetPlatformIcons: [] + m_BuildTargetBatching: [] + m_BuildTargetGraphicsAPIs: + - m_BuildTarget: WebGLSupport + m_APIs: 0b00000008000000 + m_Automatic: 1 + m_BuildTargetVRSettings: [] + m_BuildTargetEnableVuforiaSettings: [] + openGLRequireES31: 0 + openGLRequireES31AEP: 0 + m_TemplateCustomTags: {} + mobileMTRendering: + Android: 1 + iPhone: 1 + tvOS: 1 + m_BuildTargetGroupLightmapEncodingQuality: [] + m_BuildTargetGroupLightmapSettings: [] + playModeTestRunnerEnabled: 0 + runPlayModeTestAsEditModeTest: 0 + actionOnDotNetUnhandledException: 1 + enableInternalProfiler: 0 + logObjCUncaughtExceptions: 1 + enableCrashReportAPI: 0 + cameraUsageDescription: + locationUsageDescription: + microphoneUsageDescription: + switchNetLibKey: + switchSocketMemoryPoolSize: 6144 + switchSocketAllocatorPoolSize: 128 + switchSocketConcurrencyLimit: 14 + switchScreenResolutionBehavior: 2 + switchUseCPUProfiler: 0 + switchApplicationID: 0x01004b9000490000 + switchNSODependencies: + switchTitleNames_0: + switchTitleNames_1: + switchTitleNames_2: + switchTitleNames_3: + switchTitleNames_4: + switchTitleNames_5: + switchTitleNames_6: + switchTitleNames_7: + switchTitleNames_8: + switchTitleNames_9: + switchTitleNames_10: + switchTitleNames_11: + switchTitleNames_12: + switchTitleNames_13: + switchTitleNames_14: + switchTitleNames_15: + switchPublisherNames_0: + switchPublisherNames_1: + switchPublisherNames_2: + switchPublisherNames_3: + switchPublisherNames_4: + switchPublisherNames_5: + switchPublisherNames_6: + switchPublisherNames_7: + switchPublisherNames_8: + switchPublisherNames_9: + switchPublisherNames_10: + switchPublisherNames_11: + switchPublisherNames_12: + switchPublisherNames_13: + switchPublisherNames_14: + switchPublisherNames_15: + switchIcons_0: {fileID: 0} + switchIcons_1: {fileID: 0} + switchIcons_2: {fileID: 0} + switchIcons_3: {fileID: 0} + switchIcons_4: {fileID: 0} + switchIcons_5: {fileID: 0} + switchIcons_6: {fileID: 0} + switchIcons_7: {fileID: 0} + switchIcons_8: {fileID: 0} + switchIcons_9: {fileID: 0} + switchIcons_10: {fileID: 0} + switchIcons_11: {fileID: 0} + switchIcons_12: {fileID: 0} + switchIcons_13: {fileID: 0} + switchIcons_14: {fileID: 0} + switchIcons_15: {fileID: 0} + switchSmallIcons_0: {fileID: 0} + switchSmallIcons_1: {fileID: 0} + switchSmallIcons_2: {fileID: 0} + switchSmallIcons_3: {fileID: 0} + switchSmallIcons_4: {fileID: 0} + switchSmallIcons_5: {fileID: 0} + switchSmallIcons_6: {fileID: 0} + switchSmallIcons_7: {fileID: 0} + switchSmallIcons_8: {fileID: 0} + switchSmallIcons_9: {fileID: 0} + switchSmallIcons_10: {fileID: 0} + switchSmallIcons_11: {fileID: 0} + switchSmallIcons_12: {fileID: 0} + switchSmallIcons_13: {fileID: 0} + switchSmallIcons_14: {fileID: 0} + switchSmallIcons_15: {fileID: 0} + switchManualHTML: + switchAccessibleURLs: + switchLegalInformation: + switchMainThreadStackSize: 1048576 + switchPresenceGroupId: + switchLogoHandling: 0 + switchReleaseVersion: 0 + switchDisplayVersion: 1.0.0 + switchStartupUserAccount: 0 + switchTouchScreenUsage: 0 + switchSupportedLanguagesMask: 0 + switchLogoType: 0 + switchApplicationErrorCodeCategory: + switchUserAccountSaveDataSize: 0 + switchUserAccountSaveDataJournalSize: 0 + switchApplicationAttribute: 0 + switchCardSpecSize: -1 + switchCardSpecClock: -1 + switchRatingsMask: 0 + switchRatingsInt_0: 0 + switchRatingsInt_1: 0 + switchRatingsInt_2: 0 + switchRatingsInt_3: 0 + switchRatingsInt_4: 0 + switchRatingsInt_5: 0 + switchRatingsInt_6: 0 + switchRatingsInt_7: 0 + switchRatingsInt_8: 0 + switchRatingsInt_9: 0 + switchRatingsInt_10: 0 + switchRatingsInt_11: 0 + switchRatingsInt_12: 0 + switchLocalCommunicationIds_0: + switchLocalCommunicationIds_1: + switchLocalCommunicationIds_2: + switchLocalCommunicationIds_3: + switchLocalCommunicationIds_4: + switchLocalCommunicationIds_5: + switchLocalCommunicationIds_6: + switchLocalCommunicationIds_7: + switchParentalControl: 0 + switchAllowsScreenshot: 1 + switchAllowsVideoCapturing: 1 + switchAllowsRuntimeAddOnContentInstall: 0 + switchDataLossConfirmation: 0 + switchUserAccountLockEnabled: 0 + switchSystemResourceMemory: 16777216 + switchSupportedNpadStyles: 3 + switchNativeFsCacheSize: 32 + switchIsHoldTypeHorizontal: 0 + switchSupportedNpadCount: 8 + switchSocketConfigEnabled: 0 + switchTcpInitialSendBufferSize: 32 + switchTcpInitialReceiveBufferSize: 64 + switchTcpAutoSendBufferSizeMax: 256 + switchTcpAutoReceiveBufferSizeMax: 256 + switchUdpSendBufferSize: 9 + switchUdpReceiveBufferSize: 42 + switchSocketBufferEfficiency: 4 + switchSocketInitializeEnabled: 1 + switchNetworkInterfaceManagerInitializeEnabled: 1 + switchPlayerConnectionEnabled: 1 + ps4NPAgeRating: 12 + ps4NPTitleSecret: + ps4NPTrophyPackPath: + ps4ParentalLevel: 11 + ps4ContentID: ED1633-NPXX51362_00-0000000000000000 + ps4Category: 0 + ps4MasterVersion: 01.00 + ps4AppVersion: 01.00 + ps4AppType: 0 + ps4ParamSfxPath: + ps4VideoOutPixelFormat: 0 + ps4VideoOutInitialWidth: 1920 + ps4VideoOutBaseModeInitialWidth: 1920 + ps4VideoOutReprojectionRate: 60 + ps4PronunciationXMLPath: + ps4PronunciationSIGPath: + ps4BackgroundImagePath: + ps4StartupImagePath: + ps4StartupImagesFolder: + ps4IconImagesFolder: + ps4SaveDataImagePath: + ps4SdkOverride: + ps4BGMPath: + ps4ShareFilePath: + ps4ShareOverlayImagePath: + ps4PrivacyGuardImagePath: + ps4ExtraSceSysFile: + ps4NPtitleDatPath: + ps4RemotePlayKeyAssignment: -1 + ps4RemotePlayKeyMappingDir: + ps4PlayTogetherPlayerCount: 0 + ps4EnterButtonAssignment: 1 + ps4ApplicationParam1: 0 + ps4ApplicationParam2: 0 + ps4ApplicationParam3: 0 + ps4ApplicationParam4: 0 + ps4DownloadDataSize: 0 + ps4GarlicHeapSize: 2048 + ps4ProGarlicHeapSize: 2560 + ps4Passcode: frAQBc8Wsa1xVPfvJcrgRYwTiizs2trQ + ps4pnSessions: 1 + ps4pnPresence: 1 + ps4pnFriends: 1 + ps4pnGameCustomData: 1 + playerPrefsSupport: 0 + enableApplicationExit: 0 + resetTempFolder: 1 + restrictedAudioUsageRights: 0 + ps4UseResolutionFallback: 0 + ps4ReprojectionSupport: 0 + ps4UseAudio3dBackend: 0 + ps4SocialScreenEnabled: 0 + ps4ScriptOptimizationLevel: 0 + ps4Audio3dVirtualSpeakerCount: 14 + ps4attribCpuUsage: 0 + ps4PatchPkgPath: + ps4PatchLatestPkgPath: + ps4PatchChangeinfoPath: + ps4PatchDayOne: 0 + ps4attribUserManagement: 0 + ps4attribMoveSupport: 0 + ps4attrib3DSupport: 0 + ps4attribShareSupport: 0 + ps4attribExclusiveVR: 0 + ps4disableAutoHideSplash: 0 + ps4videoRecordingFeaturesUsed: 0 + ps4contentSearchFeaturesUsed: 0 + ps4CompatibilityPS5: 0 + ps4AllowPS5Detection: 0 + ps4GPU800MHz: 1 + ps4attribEyeToEyeDistanceSettingVR: 0 + ps4IncludedModules: [] + monoEnv: + splashScreenBackgroundSourceLandscape: {fileID: 0} + splashScreenBackgroundSourcePortrait: {fileID: 0} + spritePackerPolicy: + webGLMemorySize: 256 + webGLExceptionSupport: 1 + webGLNameFilesAsHashes: 0 + webGLDataCaching: 0 + webGLDebugSymbols: 0 + webGLEmscriptenArgs: + webGLModulesDirectory: + webGLTemplate: APPLICATION:Default + webGLAnalyzeBuildSize: 0 + webGLUseEmbeddedResources: 0 + webGLCompressionFormat: 2 + webGLLinkerTarget: 2 + webGLThreadsSupport: 0 + scriptingDefineSymbols: {} + platformArchitecture: {} + scriptingBackend: + Standalone: 1 + il2cppCompilerConfiguration: {} + managedStrippingLevel: {} + incrementalIl2cppBuild: {} + allowUnsafeCode: 0 + additionalIl2CppArgs: + scriptingRuntimeVersion: 1 + apiCompatibilityLevelPerPlatform: {} + m_RenderingPath: 1 + m_MobileRenderingPath: 1 + metroPackageName: ProtoPromise_Unity + metroPackageVersion: + metroCertificatePath: + metroCertificatePassword: + metroCertificateSubject: + metroCertificateIssuer: + metroCertificateNotAfter: 0000000000000000 + metroApplicationDescription: ProtoPromise_Unity + wsaImages: {} + metroTileShortName: + metroTileShowName: 0 + metroMediumTileShowName: 0 + metroLargeTileShowName: 0 + metroWideTileShowName: 0 + metroSupportStreamingInstall: 0 + metroLastRequiredScene: 0 + metroDefaultTileSize: 1 + metroTileForegroundText: 2 + metroTileBackgroundColor: {r: 0.13333334, g: 0.17254902, b: 0.21568628, a: 0} + metroSplashScreenBackgroundColor: {r: 0.12941177, g: 0.17254902, b: 0.21568628, + a: 1} + metroSplashScreenUseBackgroundColor: 0 + platformCapabilities: {} + metroTargetDeviceFamilies: {} + metroFTAName: + metroFTAFileTypes: [] + metroProtocolName: + metroCompilationOverrides: 1 + XboxOneProductId: + XboxOneUpdateKey: + XboxOneSandboxId: + XboxOneContentId: + XboxOneTitleId: + XboxOneSCId: + XboxOneGameOsOverridePath: + XboxOnePackagingOverridePath: + XboxOneAppManifestOverridePath: + XboxOneVersion: 1.0.0.0 + XboxOnePackageEncryption: 0 + XboxOnePackageUpdateGranularity: 2 + XboxOneDescription: + XboxOneLanguage: + - enus + XboxOneCapability: [] + XboxOneGameRating: {} + XboxOneIsContentPackage: 0 + XboxOneEnableGPUVariability: 0 + XboxOneSockets: {} + XboxOneSplashScreen: {fileID: 0} + XboxOneAllowedProductIds: [] + XboxOnePersistentLocalStorageSize: 0 + XboxOneXTitleMemory: 8 + xboxOneScriptCompiler: 0 + XboxOneOverrideIdentityName: + vrEditorSettings: + daydream: + daydreamIconForeground: {fileID: 0} + daydreamIconBackground: {fileID: 0} + cloudServicesEnabled: {} + luminIcon: + m_Name: + m_ModelFolderPath: + m_PortalFolderPath: + luminCert: + m_CertPath: + m_PrivateKeyPath: + luminIsChannelApp: 0 + luminVersion: + m_VersionCode: 1 + m_VersionName: + facebookSdkVersion: 7.9.4 + facebookAppId: + facebookCookies: 1 + facebookLogging: 1 + facebookStatus: 1 + facebookXfbml: 0 + facebookFrictionlessRequests: 1 + apiCompatibilityLevel: 6 + cloudProjectId: + framebufferDepthMemorylessMode: 0 + projectName: + organizationId: + cloudEnabled: 0 + enableNativePlatformBackendsForNewInputSystem: 0 + disableOldInputManagerSupport: 0 + legacyClampBlendShapeWeights: 1 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectVersion.txt b/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectVersion.txt new file mode 100644 index 00000000..da45772d --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/ProjectVersion.txt @@ -0,0 +1,2 @@ +m_EditorVersion: 2023.1.0f1 +m_EditorVersionWithRevision: 2023.1.0f1 (a008fa768e6c) diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/QualitySettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/QualitySettings.asset new file mode 100644 index 00000000..05daac3c --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/QualitySettings.asset @@ -0,0 +1,191 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!47 &1 +QualitySettings: + m_ObjectHideFlags: 0 + serializedVersion: 5 + m_CurrentQuality: 5 + m_QualitySettings: + - serializedVersion: 2 + name: Very Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 15 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 1 + textureQuality: 1 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.3 + maximumLODLevel: 0 + particleRaycastBudget: 4 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Low + pixelLightCount: 0 + shadows: 0 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 0 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 0 + lodBias: 0.4 + maximumLODLevel: 0 + particleRaycastBudget: 16 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Medium + pixelLightCount: 1 + shadows: 1 + shadowResolution: 0 + shadowProjection: 1 + shadowCascades: 1 + shadowDistance: 20 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 0 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 0 + realtimeReflectionProbes: 0 + billboardsFaceCameraPosition: 0 + vSyncCount: 1 + lodBias: 0.7 + maximumLODLevel: 0 + particleRaycastBudget: 64 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: High + pixelLightCount: 2 + shadows: 2 + shadowResolution: 1 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 40 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 2 + textureQuality: 0 + anisotropicTextures: 1 + antiAliasing: 0 + softParticles: 0 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1 + maximumLODLevel: 0 + particleRaycastBudget: 256 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Very High + pixelLightCount: 3 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 2 + shadowDistance: 70 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 1.5 + maximumLODLevel: 0 + particleRaycastBudget: 1024 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + - serializedVersion: 2 + name: Ultra + pixelLightCount: 4 + shadows: 2 + shadowResolution: 2 + shadowProjection: 1 + shadowCascades: 4 + shadowDistance: 150 + shadowNearPlaneOffset: 3 + shadowCascade2Split: 0.33333334 + shadowCascade4Split: {x: 0.06666667, y: 0.2, z: 0.46666667} + shadowmaskMode: 1 + blendWeights: 4 + textureQuality: 0 + anisotropicTextures: 2 + antiAliasing: 2 + softParticles: 1 + softVegetation: 1 + realtimeReflectionProbes: 1 + billboardsFaceCameraPosition: 1 + vSyncCount: 1 + lodBias: 2 + maximumLODLevel: 0 + particleRaycastBudget: 4096 + asyncUploadTimeSlice: 2 + asyncUploadBufferSize: 4 + resolutionScalingFixedDPIFactor: 1 + excludedTargetPlatforms: [] + m_PerPlatformDefaultQuality: + Android: 2 + Nintendo 3DS: 5 + Nintendo Switch: 5 + PS4: 5 + PSM: 5 + PSP2: 2 + Standalone: 5 + Tizen: 2 + WebGL: 3 + WiiU: 5 + Windows Store Apps: 5 + XboxOne: 5 + iPhone: 2 + tvOS: 2 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/TagManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/TagManager.asset new file mode 100644 index 00000000..1c92a784 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/TagManager.asset @@ -0,0 +1,43 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!78 &1 +TagManager: + serializedVersion: 2 + tags: [] + layers: + - Default + - TransparentFX + - Ignore Raycast + - + - Water + - UI + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + m_SortingLayers: + - name: Default + uniqueID: 0 + locked: 0 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/TimeManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/TimeManager.asset new file mode 100644 index 00000000..558a017e --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/TimeManager.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!5 &1 +TimeManager: + m_ObjectHideFlags: 0 + Fixed Timestep: 0.02 + Maximum Allowed Timestep: 0.33333334 + m_TimeScale: 1 + Maximum Particle Timestep: 0.03 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/UnityConnectSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/UnityConnectSettings.asset new file mode 100644 index 00000000..3da14d5b --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/UnityConnectSettings.asset @@ -0,0 +1,34 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!310 &1 +UnityConnectSettings: + m_ObjectHideFlags: 0 + m_Enabled: 0 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + m_TestInitMode: 0 + CrashReportingSettings: + m_EventUrl: https://perf-events.cloud.unity3d.com/api/events/crashes + m_NativeEventUrl: https://perf-events.cloud.unity3d.com/symbolicate + m_Enabled: 0 + m_CaptureEditorExceptions: 1 + UnityPurchasingSettings: + m_Enabled: 0 + m_TestMode: 0 + UnityAnalyticsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_TestEventUrl: + m_TestConfigUrl: + UnityAdsSettings: + m_Enabled: 0 + m_InitializeOnStartup: 1 + m_TestMode: 0 + m_IosGameId: + m_AndroidGameId: + m_GameIds: {} + m_GameId: + PerformanceReportingSettings: + m_Enabled: 0 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/VFXManager.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/VFXManager.asset new file mode 100644 index 00000000..6e0eaca4 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/VFXManager.asset @@ -0,0 +1,11 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!937362698 &1 +VFXManager: + m_ObjectHideFlags: 0 + m_IndirectShader: {fileID: 0} + m_CopyBufferShader: {fileID: 0} + m_SortShader: {fileID: 0} + m_RenderPipeSettingsPath: + m_FixedTimeStep: 0.016666668 + m_MaxDeltaTime: 0.05 diff --git a/.github/unityci/Unity_2023_Settings/ProjectSettings/VersionControlSettings.asset b/.github/unityci/Unity_2023_Settings/ProjectSettings/VersionControlSettings.asset new file mode 100644 index 00000000..246bcd47 --- /dev/null +++ b/.github/unityci/Unity_2023_Settings/ProjectSettings/VersionControlSettings.asset @@ -0,0 +1,9 @@ +%YAML 1.1 +%TAG !u! tag:unity3d.com,2011: +--- !u!890905787 &1 +VersionControlSettings: + m_ObjectHideFlags: 0 + m_Mode: Visible Meta Files + m_CollabEditorSettings: + inProgressEnabled: 1 + m_TrackPackagesOutsideProject: 0 diff --git a/.github/workflows/activation.yml b/.github/workflows/activation.yml deleted file mode 100644 index 7f6ab309..00000000 --- a/.github/workflows/activation.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: Acquire activation file -on: workflow_dispatch -jobs: - activation: - name: Request manual activation file - runs-on: ubuntu-latest - steps: - # Request manual activation file - - name: Request manual activation file - id: getManualLicenseFile - uses: game-ci/unity-request-activation-file@v2 - with: - unityVersion: 2020.3.17f1 - # Upload artifact (Unity_v20XX.X.XXXX.alf) - - name: Expose as artifact - uses: actions/upload-artifact@v2 - with: - name: ${{ steps.getManualLicenseFile.outputs.filePath }} - path: ${{ steps.getManualLicenseFile.outputs.filePath }} \ No newline at end of file diff --git a/.github/workflows/csproj-builds.yml b/.github/workflows/csproj-builds.yml index 867ccb29..abc5cf75 100644 --- a/.github/workflows/csproj-builds.yml +++ b/.github/workflows/csproj-builds.yml @@ -10,26 +10,35 @@ concurrency: jobs: csproj-build: + name: ${{ matrix.config }}-${{ matrix.devMode.name }} runs-on: ubuntu-latest strategy: fail-fast: false matrix: config: [Release, Debug] - developerMode: [true, false] + devMode: + - { + name: devMode, + value: true + } + - { + name: userMode, + value: false + } steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 8.X - name: Build Core run: | - dotnet build -c ${{ matrix.config }} /p:DeveloperMode=${{ matrix.developerMode }} /p:IsNugetBuild=true + dotnet build -c ${{ matrix.config }} /p:DeveloperMode=${{ matrix.devMode.value }} /p:IsNugetBuild=true timeout-minutes: 10 - name: Build Analyzer diff --git a/.github/workflows/dotnet-core-tests.yml b/.github/workflows/dotnet-core-tests.yml index 74bda378..72b4b5b6 100644 --- a/.github/workflows/dotnet-core-tests.yml +++ b/.github/workflows/dotnet-core-tests.yml @@ -8,7 +8,7 @@ concurrency: jobs: dotnet-core-tests: - name: Test (${{ matrix.config }}, ${{ matrix.objectPooling.name }}, ${{ matrix.devMode.name }}) + name: ${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }} runs-on: ubuntu-latest strategy: @@ -43,32 +43,32 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 8.X - name: Run Analyzer Tests run: | - dotnet test ./ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj -c ${{ matrix.config }} -f net6.0 --logger "Console;Verbosity=normal" --logger "trx;logfilename=dotnet-test-results-net6.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }}-analyzer.trx" -p:ExtraDefineConstants=${{ matrix.objectPooling.symbol }} -p:DeveloperMode=${{ matrix.devMode.value }} --results-directory "./TestResults" + dotnet test ./ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj -c ${{ matrix.config }} -f net8.0 --logger "Console;Verbosity=normal" --logger "trx;logfilename=dotnet-test-results-net8.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }}-analyzer.trx" -p:ExtraDefineConstants=${{ matrix.objectPooling.symbol }} -p:DeveloperMode=${{ matrix.devMode.value }} --results-directory "./TestResults" timeout-minutes: 10 - name: Run Core Tests run: | - dotnet test ./ProtoPromiseTests/ProtoPromiseTests.csproj -c ${{ matrix.config }} -f net6.0 --logger "Console;Verbosity=normal" --logger "trx;logfilename=dotnet-test-results-net6.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }}-core.trx" -p:ExtraDefineConstants=${{ matrix.objectPooling.symbol }} -p:DeveloperMode=${{ matrix.devMode.value }} --results-directory "./TestResults" - timeout-minutes: 180 + dotnet test ./ProtoPromiseTests/ProtoPromiseTests.csproj -c ${{ matrix.config }} -f net8.0 --logger "Console;Verbosity=normal" --logger "trx;logfilename=dotnet-test-results-net8.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }}-core.trx" -p:ExtraDefineConstants=${{ matrix.objectPooling.symbol }} -p:DeveloperMode=${{ matrix.devMode.value }} --results-directory "./TestResults" + timeout-minutes: 120 - uses: dorny/test-reporter@v1 if: always() with: - name: dotnet-test-results-net6.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }} + name: dotnet-test-results-net8.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }} path: TestResults/*.trx reporter: dotnet-trx - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: - name: dotnet-test-results-net6.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }} + name: dotnet-test-results-net8.0-${{ matrix.config }}-${{ matrix.objectPooling.name }}-${{ matrix.devMode.name }} path: TestResults diff --git a/.github/workflows/publish-nuget-analyzer.yml b/.github/workflows/publish-nuget-analyzer.yml index edaacec4..1f0efc5d 100644 --- a/.github/workflows/publish-nuget-analyzer.yml +++ b/.github/workflows/publish-nuget-analyzer.yml @@ -13,17 +13,17 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup nuget - uses: nuget/setup-nuget@v1 + uses: nuget/setup-nuget@v2 with: nuget-api-key: ${{ secrets.NUGET_API_KEY }} - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 8.X - name: Build Package run: dotnet build ./ProtoPromise.Analyzer/ProtoPromise.Analyzer -c Release /p:PackageVersion=${{ github.event.inputs.version }} diff --git a/.github/workflows/publish-nuget.yml b/.github/workflows/publish-nuget.yml index 87c79cfc..178c0f73 100644 --- a/.github/workflows/publish-nuget.yml +++ b/.github/workflows/publish-nuget.yml @@ -10,7 +10,7 @@ jobs: timeout-minutes: 15 steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Verify commit exists in origin/master run: | @@ -79,9 +79,9 @@ jobs: nuget-api-key: ${{ secrets.NUGET_API_KEY }} - name: Setup .NET Core SDK - uses: actions/setup-dotnet@v3 + uses: actions/setup-dotnet@v4 with: - global-json-file: global.json + dotnet-version: 8.X - name: Build Debug run: dotnet build -c Debug /p:IsNugetBuild=true diff --git a/.github/workflows/unity-builds.yml b/.github/workflows/unity-builds.yml index 8aa571e9..06bad3d6 100644 --- a/.github/workflows/unity-builds.yml +++ b/.github/workflows/unity-builds.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Rewrite ProjectSettings run: | @@ -42,8 +42,10 @@ jobs: sed -i "{s/$DefineOriginal/$DefineReplace/g}" ProtoPromise_Unity/ProjectSettings/ProjectSettings.asset - name: Build Project - uses: game-ci/unity-builder@v3 + uses: game-ci/unity-builder@v4 env: + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE_2018_4_36F1_UBUNTU }} with: projectPath: ProtoPromise_Unity diff --git a/.github/workflows/unity-tests.yml b/.github/workflows/unity-tests.yml index 9d38e5dc..68efb3e4 100644 --- a/.github/workflows/unity-tests.yml +++ b/.github/workflows/unity-tests.yml @@ -8,7 +8,7 @@ concurrency: jobs: unity-tests: - name: Test (${{ matrix.testMode.name }}, ${{ matrix.mode.name }}, ${{ matrix.objectPooling.name }}, ${{ matrix.unityVersion }}) + name: ${{ matrix.unity.major }}-${{ matrix.configuration.name }}-${{ matrix.objectPooling.name }}-${{ matrix.testMode.name }} runs-on: ubuntu-latest strategy: @@ -18,12 +18,10 @@ jobs: - { name: Editor, value: PlayMode, - buildTargetId: 1 } - { name: Standalone, value: Standalone, - buildTargetId: 1 } # Not testing WebGL as unity-test-runner does not support it. #- { @@ -31,7 +29,7 @@ jobs: # value: WebGL, # buildTargetId: 13 # } - mode: + configuration: - { name: Release, symbol: PROTO_PROMISE_DEBUG_DISABLE @@ -49,86 +47,96 @@ jobs: name: Pool_Disabled, symbol: PROTO_PROMISE_POOL_DISABLE } - # Editor uses 2018.4 to test netstandard2.0 in Mono. - # Standalone uses 2019.4 and 2023.1 to test IL2CPP with netstandard2.0 and netstandard2.1. - unityVersion: [2018.4.36f1, 2019.4.40f1, 2023.1.0f1] + # Editor uses 2018 to test netstandard2.0 in Mono. + # Standalone uses 2019 and 2023 to test IL2CPP with netstandard2.0 and netstandard2.1. + unity: + - { + major: 2018, + version: 2018.4.36f1, + } + - { + major: 2019, + version: 2019.4.40f1, + } + - { + major: 2023, + version: 2023.1.0f1, + } exclude: # DEBUG mode forces pooling disabled. - { - mode: { name: Debug }, + configuration: { name: Debug }, objectPooling: { name: Pool_Enabled } } # Editor tests only run in 2018.4 to keep CI times down. - { testMode: { name: Editor }, - unityVersion: 2019.4.40f1 + unity: { major: 2019 } } - { testMode: { name: Editor }, - unityVersion: 2023.1.0f1 + unity: { major: 2023 } } # Standalone with IL2CPP can only be built with 2019.4+ (unity-builder docker images constraint). - { testMode: { name: Standalone }, - unityVersion: 2018.4.36f1 + unity: { major: 2018 } } + include: + # id 1 for old Unity versions, Standalone for new Unity versions. + # This is hard-coded for Standalone, since we can't run WebGL tests in CI. + - unity: { major: 2018 } + buildTargetId: 1 + - unity: { major: 2019 } + buildTargetId: 1 + - unity: { major: 2023 } + buildtargetid: standalone + steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: Copy Project Settings + run: cp -a .github/unityci/Unity_${{ matrix.unity.major }}_Settings/. ProtoPromise_Unity/ - name: Rewrite ProjectSettings run: | - DefineSymbols="${{ matrix.mode.symbol }};${{ matrix.objectPooling.symbol }}" DefineOriginal=" scriptingDefineSymbols: {}" - DefineReplace=" scriptingDefineSymbols: \\n ${{ matrix.testMode.buildTargetId }}: $DefineSymbols" + DefineReplace=" scriptingDefineSymbols: \\n ${{ matrix.buildTargetId }}: ${{ matrix.configuration.symbol }};${{ matrix.objectPooling.symbol }}" sed -i "{s/$DefineOriginal/$DefineReplace/g}" ProtoPromise_Unity/ProjectSettings/ProjectSettings.asset + # Linux IL2CPP builds consume extra disk space, so we free up some disk space to prevent the build from failing. + - name: Delete Extra ProjectSettings + run: rm -r -f .github/unityci + if: matrix.testMode.name == 'Standalone' + + - uses: jlumbroso/free-disk-space@v1.3.1 + if: matrix.testMode.name == 'Standalone' + - name: Run tests id: tests - uses: game-ci/unity-test-runner@v3 + uses: game-ci/unity-test-runner@v4 env: + UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }} + UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }} UNITY_LICENSE: ${{ secrets.UNITY_LICENSE_2018_4_36F1_UBUNTU }} with: projectPath: ProtoPromise_Unity testMode: ${{ matrix.testMode.value }} - unityVersion: ${{ matrix.unityVersion }} - timeout-minutes: 180 - - # Workaround for NUnit XML (see https://github.com/dorny/test-reporter/issues/98#issuecomment-867106931) - - name: Install NUnit - if: always() - run: | - nuget install NUnit.Console -Version 3.12.0 - - - name: Fetch transform code - if: always() - run: | - wget https://raw.githubusercontent.com/nunit/nunit-transforms/master/nunit3-junit/nunit3-junit.xslt - shell: bash - - - name: Transform NUnit3 to JUnit - if: always() - run: | - Get-ChildItem . -Filter artifacts/*.xml | Foreach-Object { - $xml = Resolve-Path $_.FullName - $output = Join-Path ($pwd) ($_.BaseName + '_junit.xml') - $xslt = New-Object System.Xml.Xsl.XslCompiledTransform; - $xslt.Load("nunit3-junit.xslt"); - $xslt.Transform($xml, $output); - } - shell: pwsh + unityVersion: ${{ matrix.unity.version }} + timeout-minutes: 120 - - uses: dorny/test-reporter@v1 + - uses: dorny/test-reporter@main if: always() with: - name: unity-test-results-${{ matrix.testMode.name }}-${{ matrix.mode.name }}-${{ matrix.objectPooling.name }}-${{ matrix.unityVersion }} - path: "*_junit.xml" - reporter: java-junit + name: unity-test-results-${{ matrix.unity.major }}-${{ matrix.testMode.name }}-${{ matrix.configuration.name }}-${{ matrix.objectPooling.name }} + path: 'artifacts/*.xml' + reporter: dotnet-nunit - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 if: always() with: - name: unity-test-results-${{ matrix.testMode.name }}-${{ matrix.mode.name }}-${{ matrix.objectPooling.name }}-${{ matrix.unityVersion }} + name: unity-test-results-${{ matrix.unity.major }}-${{ matrix.testMode.name }}-${{ matrix.configuration.name }}-${{ matrix.objectPooling.name }} path: artifacts diff --git a/.gitignore b/.gitignore index 8a263cba..33c4bbad 100644 --- a/.gitignore +++ b/.gitignore @@ -98,8 +98,10 @@ PublishScripts/ *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* -# except build/, which is used as an MSBuild target. +# except build/, which is used as an MSBuild target !**/packages/build/ +# and Unity's manifest.json. +!**/Packages/manifest.json # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files diff --git a/Docs/Changelog/v3.1.0.md b/Docs/Changelog/v3.1.0.md new file mode 100644 index 00000000..cb88d4c5 --- /dev/null +++ b/Docs/Changelog/v3.1.0.md @@ -0,0 +1,19 @@ +# Change Log + +## v3.1.0 - June 30, 2024 + +Enhancements: + +- Added structured concurrency promise groups. +- Added `Promise().Each` APIs. +- Added `Promise().GetRetainer` APIs. +- Added nuget configuration override. + +Fixes: + +- Fixed some `Promise.AllSettled` APIs. +- Fixed circular await detection with `AsyncEnumerable.Merge` APIs. + +Deprecated: + +- Deprecated `Promise().{Preserve, Duplicate}` APIs. \ No newline at end of file diff --git a/Docs/Guides/combine-async-operations.md b/Docs/Guides/combine-async-operations.md index 2fe5ebb1..f45c543d 100644 --- a/Docs/Guides/combine-async-operations.md +++ b/Docs/Guides/combine-async-operations.md @@ -1,5 +1,7 @@ # Combining Multiple Async Operations +Note: It is recommended to use [Structured Concurrency Groups](structured-concurrency.md) instead of these methods. + ## All The `All` function combines multiple async operations that are currently running. It converts a collection of promises or a variable length parameter list of promises into a single promise, and if those promises are non-void, it yields a list containing the results of those promises in the same order. @@ -79,4 +81,22 @@ Promise.Race(Download("http://www.google.com"), Download("http://www.bing.com")) ## First -The `First` function is almost idential to `Race` except that if a promise is rejected or canceled, the First promise will remain pending until one of the input promises is resolved or they are all rejected/canceled. \ No newline at end of file +The `First` function is almost idential to `Race` except that if a promise is rejected or canceled, the First promise will remain pending until one of the input promises is resolved or they are all rejected/canceled. + +## Each + +The `Each` function is used to combine multiple async operations into an `AsyncEnumerable` that will yield each operation's result in the order that they complete. + +```cs +await foreach (var downloadResult in Promise.Each(Download("http://www.google.com"), Download("http://www.bing.com"))) +{ + if (downloadResult.State == Promise.State.Resolved) + { + Console.WriteLine(downloadResult.Value); // Print the HTML. + } + else if (downloadResult.State == Promise.State.Rejected) + { + Console.WriteLine(downloadResult.Reason); // Print the reject reason. + } +} +``` \ No newline at end of file diff --git a/Docs/Guides/configuration.md b/Docs/Guides/configuration.md index 469a3cad..6724464a 100644 --- a/Docs/Guides/configuration.md +++ b/Docs/Guides/configuration.md @@ -23,6 +23,12 @@ This isn't really a config option, but if you have object pooling enabled, you c ## Compiler Options -If you're compiling from source (not from dll), you can configure some compilation options. +If you're compiling from source (like in Unity Editor): -By default, debug options are tied to the `DEBUG` compiler symbol, which is defined by default in the Unity Editor and not defined in release builds. You can override that by defining `PROTO_PROMISE_DEBUG_ENABLE` to force debugging on in release builds, or `PROTO_PROMISE_DEBUG_DISABLE` to force debugging off in debug builds (or in the Unity Editor). If both symbols are defined, `ENABLE` takes precedence. \ No newline at end of file +By default, debug options are tied to the `DEBUG` compiler symbol, which is defined by default in the Unity Editor and not defined in release builds. You can override that by defining `PROTO_PROMISE_DEBUG_ENABLE` to force debugging on in release builds, or `PROTO_PROMISE_DEBUG_DISABLE` to force debugging off in debug builds (or in the Unity Editor). If both symbols are defined, `ENABLE` takes precedence. + +## Nuget Options + +If you're using the Nuget package (`` in your csproj): + +You can override which dll will be used by setting the `ProtoPromiseConfiguration` property. Nested under a ``, add `Release` to force it to use the Release dll, or `Debug` to force it to use the Debug dll. If the property is not set, or is set to any other value, the default behavior of choosing the dll based on your build configuration will be used. This option requires ProtoPromise v3.1.0 or newer. \ No newline at end of file diff --git a/Docs/Guides/multiple-consumer.md b/Docs/Guides/multiple-consumer.md index 7daa3e1e..d4ef1efe 100644 --- a/Docs/Guides/multiple-consumer.md +++ b/Docs/Guides/multiple-consumer.md @@ -1,7 +1,4 @@ # Multiple Consumers -Most promises can only be awaited once, and if they are not awaited, they must be returned or forgotten (see [Forget](the-basics.md#forget)). -You can preserve a promise so that it can be awaited multiple times via the `promise.Preserve()` API. When you are finished with the promise, you must call `promise.Forget()`. -Callbacks added to a preserved promise will be invoked in the order that they are added. - -Note: a preserved promise should not be returned from a public API, because the consumer could immediately call `Forget()` and invalidate the promise. Instead, you should use `promise.Duplicate()` to get a promise that will adopt its state, but can only be awaited once. \ No newline at end of file +Promises may only be awaited once, and if they are not awaited, they must be returned or forgotten (see [Forget](the-basics.md#forget)). +You may wish to cache the result of the promise so that multiple consumers can retrieve the same result. You can do so via the `promise.GetRetainer()` API, which returns a `Promise.Retainer` or `Promise.Retainer`. You can await the retainer multiple times until it is disposed. You can `await` it directly in async functions, or you can call `retainer.WaitAsync()` to get a promise that adopts the state of the retained promise. Dispose the retainer when you are finished with it. \ No newline at end of file diff --git a/Docs/Guides/structured-concurrency.md b/Docs/Guides/structured-concurrency.md new file mode 100644 index 00000000..91a1caff --- /dev/null +++ b/Docs/Guides/structured-concurrency.md @@ -0,0 +1,104 @@ +# Structured Concurrency + +Structured concurrency is a pattern that makes it simple to reason about multiple async operations running concurrently, and not needing to worry about orphaned operations continuing to run in the background after the current async context has continued. We implement this pattern via promise groups. A group is created which owns a `CancelationToken`, then async operations are started by passing in the token, and the promises are added to the group. The entire group is then awaited, which will only continue when all of the added promises have completed. If the group is expected to complete early, the `CancelationToken` will be canceled. + +## PromiseAllGroup + +`PromiseAllGroup` combines multiple async operations of a single type. When all promises have completed successfully, it yields a list containing the results of those promises in the same order they were added. If any promise is rejected, the group will be rejected with an `AggregateException` containing all of the rejections. + +```cs +var pages = await PromiseAllGroup.New(cancelationToken, out var groupCancelationToken) + .Add(Download("http://www.google.com", groupCancelationToken)) + .Add(Download("http://www.bing.com", groupCancelationToken)) + .WaitAsync(); +foreach (var link in pages.SelectMany(page => ExtractAllLinks(page))) +{ + Console.WriteLine(link); +} +``` + +## PromiseMergeGroup + +`PromiseMergeGroup` combines multiple async operations of one or more types. The group's `WaitAsync()` method either returns a void `Promise` if only void promises were added to it, or a `Promies` that yields a `ValueTuple<>` containing the type of each promise in the order it was added. When all promises have completed successfully, the group will be resolved with the appropriate value. If any promise is rejected, the group will be rejected with an `AggregateException` containing all of the rejections. + +```cs +var (page1, page2) = await PromiseMergeGroup.New(cancelationToken, out var groupCancelationToken) + .Add(Download("http://www.google.com", groupCancelationToken)) + .Add(Download("http://www.bing.com", groupCancelationToken)) + .WaitAsync(); +foreach (var link in ExtractAllLinks(page1)) +{ + Console.WriteLine(link); +} +foreach (var link in ExtractAllLinks(page2)) +{ + Console.WriteLine(link); +} +``` + +## PromiseAllResultsGroup and PromiseMergeResultsGroup + +The `PromiseAllResultsGroup` and `PromiseMergeResultsGroup` groups behave very similar to `PromiseAllGroup` and `PromiseMergeGroup`, except they yield a list/tuple of `ResultContainer`s instead of the raw type, and they won't be rejected with an `AggregateException` if any of the promises are rejected. + +```cs +var (googleResult, imageResult) = await PromiseMergeGroup.New(cancelationToken, out var groupCancelationToken) + .Add(Download("http://www.google.com", groupCancelationToken)) + .Add(DownloadImage("http://www.example.com/image.jpg", groupCancelationToken)) + .WaitAsync(); +if (googleResult.State == Promise.State.Resolved) +{ + PrintLinks(googleResult.Value); +} +else if (googleResult.State == Promise.State.Rejected) +{ + Console.WriteLine(googleResult.Reason); +} + +if (imageResult.State == Promise.State.Resolved) +{ + image.SetTexture(imageResult.Value); +} +else if (imageResult.State == Promise.State.Rejected) +{ + Console.WriteLine(imageResult.Reason); +} +``` + +## PromiseRaceGroup + +`PromiseRaceGroup` races multiple async operations of a single type. If any promise is resolved, the group will be resolved with the result of the promise that resolved first. If no promises are resolved and any promise is rejected, the group will be rejected with an `AggregateException` containing all of the rejections. + +```cs +var page = await PromiseRaceGroup.New(cancelationToken, out var groupCancelationToken) + .Add(Download("http://www.google.com", groupCancelationToken)) + .Add(Download("http://www.bing.com", groupCancelationToken)) + .WaitAsync(); +Console.WriteLine(page); // Print the page that was downloaded first. +``` + +## PromiseEachGroup + +`PromiseEachGroup` combines multiple async operations of a single type into an `AsyncEnumerable` that will yield each operation's result in the order that they complete. + +```cs +var asyncEnumerable = PromiseEachGroup.New(cancelationToken, out var groupCancelationToken) + .Add(Download("http://www.google.com", groupCancelationToken)) + .Add(Download("http://www.bing.com", groupCancelationToken)) + .GetAsyncEnumerable(); +// Print each page in the order that their downloads complete. +await foreach (var downloadResult in asyncEnumerable) +{ + if (downloadResult.State == Promise.State.Resolved) + { + Console.WriteLine(downloadResult.Value); // Print the HTML. + } + else if (downloadResult.State == Promise.State.Rejected) + { + Console.WriteLine(downloadResult.Reason); // Print the reject reason. + } + else + { + Console.WriteLine("Download was canceled"); + } +} +``` \ No newline at end of file diff --git a/Package/Core/Cancelations/CancelationToken.cs b/Package/Core/Cancelations/CancelationToken.cs index e2286d93..c912f676 100644 --- a/Package/Core/Cancelations/CancelationToken.cs +++ b/Package/Core/Cancelations/CancelationToken.cs @@ -68,7 +68,7 @@ public void ThrowIfCancelationRequested() { if (IsCancelationRequested) { - throw Internal.CanceledExceptionInternal.GetOrCreate(); + throw Promise.CancelException(); } } diff --git a/Package/Core/Cancelations/Internal/CancelationInternal.cs b/Package/Core/Cancelations/Internal/CancelationInternal.cs index 99e3d633..f817936d 100644 --- a/Package/Core/Cancelations/Internal/CancelationInternal.cs +++ b/Package/Core/Cancelations/Internal/CancelationInternal.cs @@ -100,7 +100,7 @@ private CancelationRef(byte userRetainIncrementor) if (_userRetainCounter > 0) { // CancelationToken wasn't released. - string message = "A CancelationToken's resources were garbage collected without being released. You must release all IRetainable objects that you have retained."; + string message = "A CancelationToken's resources were garbage collected without being released. You must release all CancelationTokens that you have retained."; ReportRejection(new UnreleasedObjectException(message), this); } // We don't check the disposed state if this was linked to a System.Threading.CancellationToken. @@ -491,17 +491,13 @@ internal static bool TryDispose(CancelationRef _this, int sourceId) internal bool TryDispose(int sourceId) { _smallFields._locker.Enter(); - if (sourceId != SourceId) + if (!TryIncrementSourceId(sourceId)) { _smallFields._locker.Exit(); return false; } ThrowIfInPool(this); - unchecked - { - ++_sourceId; - } if (_state == State.Pending) { _state = State.Disposed; @@ -511,6 +507,10 @@ internal bool TryDispose(int sourceId) return true; } + [MethodImpl(InlineOption)] + internal bool TryIncrementSourceId(int sourceId) + => Interlocked.CompareExchange(ref _sourceId, unchecked(sourceId + 1), sourceId) == sourceId; + private void UnregisterAll() { // If the previous points to this, no registrations exist. @@ -634,7 +634,14 @@ private void ResetAndRepool() internal void Cancel() { - TrySetCanceled(SourceId); + // Same as TrySetCanceled, but without checking the SourceId. + _smallFields._locker.Enter(); + if (_state != State.Pending) + { + _smallFields._locker.Exit(); + return; + } + InvokeCallbacksAlreadyLocked(); } #if !PROTO_PROMISE_DEVELOPER_MODE @@ -1112,8 +1119,8 @@ private static void WaitForInvokeComplete(CancelationRef parent, CancelationCall // Queue the check to happen again on a background thread. // Force async so the current thread will be yielded if this is already being executed on a background thread. // This is recursive, but it's done so asynchronously so it will never cause StackOverflowException. - Promise.Run(ValueTuple.Create(parent, node, nodeId, tokenId, deferred), - cv => WaitForInvokeComplete(cv.Item1, cv.Item2, cv.Item3, cv.Item4, cv.Item5), + Promise.Run((parent, node, nodeId, tokenId, deferred), + cv => WaitForInvokeComplete(cv.parent, cv.node, cv.nodeId, cv.tokenId, cv.deferred), Promise.Config.BackgroundContext, forceAsync: true) .Forget(); } diff --git a/Package/Core/Collections/TempCollection.cs b/Package/Core/Collections/TempCollection.cs index 9651eb8c..13f87c7c 100644 --- a/Package/Core/Collections/TempCollection.cs +++ b/Package/Core/Collections/TempCollection.cs @@ -327,8 +327,7 @@ public void Dispose() #endif Array.Clear(_items, 0, _count); ArrayPool.Shared.Return(_items, false); - _items = null; - _count = -1; + this = default; } [MethodImpl(Internal.InlineOption)] diff --git a/Package/Core/InternalShared/DebugInternal.cs b/Package/Core/InternalShared/DebugInternal.cs index c94ad744..1bba1da7 100644 --- a/Package/Core/InternalShared/DebugInternal.cs +++ b/Package/Core/InternalShared/DebugInternal.cs @@ -96,10 +96,16 @@ internal static ITraceable GetCurrent(int skipFrames) private const ITraceable SynchronousTraceable = null; #endif + static partial void SetPrevious(this PromiseRefBase promise, PromiseRefBase previous); static partial void SetCreatedStacktrace(ITraceable traceable, int skipFrames); static partial void SetCurrentInvoker(ITraceable current); static partial void ClearCurrentInvoker(); + #if PROMISE_DEBUG + // We only set _previous to support circular await detection in DEBUG mode. + static partial void SetPrevious(this PromiseRefBase promise, PromiseRefBase previous) + => promise._previous = previous; + static partial void SetCreatedStacktrace(ITraceable traceable, int skipFrames) => SetCreatedStacktraceImpl(traceable, skipFrames); @@ -148,6 +154,15 @@ internal static void ValidateArgument(TArg arg, string argName, int skipFr throw new ArgumentNullException(argName, null, GetFormattedStacktrace(skipFrames + 1)); } } + internal static void ValidateArgument(Promise arg, string argName, int skipFrames) + { + if (!arg.IsValid) + { + throw new InvalidArgumentException(argName, + "Promise is invalid. Call `GetRetainer()` if you intend to await multiple times.", + Internal.GetFormattedStacktrace(skipFrames + 1)); + } + } internal static string FormatStackTrace(IEnumerable stackTraces) { @@ -234,8 +249,7 @@ internal static void ValidateOperation(Promise promise, int skipFrames) if (!promise.IsValid) { throw new InvalidOperationException("Promise is invalid." + - " Call `Preserve()` if you intend to add multiple callbacks or await multiple times on a single promise instance." + - " Remember to call `Forget()` when you are finished with it!", + " Call `GetRetainer()` if you intend to await multiple times.", GetFormattedStacktrace(skipFrames + 1)); } } @@ -468,25 +482,15 @@ static partial void ValidateArgument(TArg arg, string argName, int skipFra => Internal.ValidateArgument(arg, argName, skipFrames + 1); static partial void ValidateArgument(Promise arg, string argName, int skipFrames) - { - if (!arg.IsValid) - { - throw new InvalidArgumentException(argName, - "Promise is invalid." + - " Call `Preserve()` if you intend to add multiple callbacks or await multiple times on a single promise instance." + - " Remember to call `Forget()` when you are finished with it!", - Internal.GetFormattedStacktrace(skipFrames + 1)); - } - } + => Internal.ValidateArgument(arg, argName, skipFrames + 1); static partial void ValidateElement(Promise promise, string argName, int skipFrames) { if (!promise.IsValid) { throw new InvalidElementException(argName, - string.Format("A promise is invalid in {0}." + - " Call `Preserve()` if you intend to add multiple callbacks or await multiple times on a single promise instance." + - " Remember to call `Forget()` when you are finished with it!", argName), + $"A promise is invalid in {argName}." + + " Call `GetRetainer()` if you intend to await multiple times.", Internal.GetFormattedStacktrace(skipFrames + 1)); } } @@ -508,25 +512,15 @@ static partial void ValidateArgument(TArg arg, string argName, int skipFra => Internal.ValidateArgument(arg, argName, skipFrames + 1); static partial void ValidateArgument(Promise arg, string argName, int skipFrames) - { - if (!arg.IsValid) - { - throw new InvalidArgumentException(argName, - "Promise is invalid." + - " Call `Preserve()` if you intend to add multiple callbacks or await multiple times on a single promise instance." + - " Remember to call `Forget()` when you are finished with it!", - Internal.GetFormattedStacktrace(skipFrames + 1)); - } - } + => Internal.ValidateArgument(arg, argName, skipFrames + 1); static partial void ValidateElement(Promise promise, string argName, int skipFrames) { if (!promise.IsValid) { throw new InvalidElementException(argName, - string.Format("A promise is invalid in {0}." + - " Call `Preserve()` if you intend to add multiple callbacks or await multiple times on a single promise instance." + - " Remember to call `Forget()` when you are finished with it!", argName), + $"A promise is invalid in {argName}." + + " Call `GetRetainer()` if you intend to await multiple times.", Internal.GetFormattedStacktrace(skipFrames + 1)); } } diff --git a/Package/Core/InternalShared/ExceptionsInternal.cs b/Package/Core/InternalShared/ExceptionsInternal.cs index 6c5b89dc..b69464ea 100644 --- a/Package/Core/InternalShared/ExceptionsInternal.cs +++ b/Package/Core/InternalShared/ExceptionsInternal.cs @@ -4,6 +4,8 @@ #undef PROMISE_DEBUG #endif +#pragma warning disable IDE0090 // Use 'new(...)' + using System; using System.Diagnostics; using System.Runtime.ExceptionServices; @@ -17,29 +19,24 @@ partial class Internal #endif internal sealed class UnhandledExceptionInternal : UnhandledException, IRejectContainer, IRejectionToContainer, ICantHandleException { - internal UnhandledExceptionInternal(object value, string message, string stackTrace, Exception innerException) : - base(value, message, stackTrace, innerException) + internal UnhandledExceptionInternal(object value, string message, string stackTrace, Exception innerException) + : base(value, message, stackTrace, innerException) { } void ICantHandleException.ReportUnhandled(ITraceable traceable) - { - ReportUnhandledException(this); - } + => ReportUnhandledException(this); void IRejectContainer.ReportUnhandled() - { - ReportUnhandledException(this); - } + => ReportUnhandledException(this); ExceptionDispatchInfo IRejectContainer.GetExceptionDispatchInfo() - { - return ExceptionDispatchInfo.Capture(Value as Exception ?? this); - } + => ExceptionDispatchInfo.Capture(Value as Exception ?? this); IRejectContainer IRejectionToContainer.ToContainer(ITraceable traceable) - { - return this; - } + => this; + + Exception IRejectContainer.GetValueAsException() + => Value as Exception ?? this; } #if !PROTO_PROMISE_DEVELOPER_MODE @@ -47,18 +44,18 @@ IRejectContainer IRejectionToContainer.ToContainer(ITraceable traceable) #endif internal sealed class CanceledExceptionInternal : CanceledException { -#if !PROMISE_DEBUG - private static readonly CanceledExceptionInternal s_instance = new CanceledExceptionInternal("Operation was canceled."); -#endif - + // Old Unity runtime has a bug where stack traces are continually appended to the exception, causing a memory leak and runtime slowdowns. + // To avoid the issue, we only use a singleton in runtimes where the bug is not present. +#if PROMISE_DEBUG || NETSTANDARD2_0 || (UNITY_2018_3_OR_NEWER && !UNITY_2021_2_OR_NEWER) + // Don't re-use instance in DEBUG mode so users can read its stacktrace on any thread. internal static CanceledExceptionInternal GetOrCreate() - { -#if PROMISE_DEBUG - return new CanceledExceptionInternal("Operation was canceled."); // Don't re-use instance in DEBUG mode so users can read its stacktrace on any thread. + => new CanceledExceptionInternal("Operation was canceled."); #else - return s_instance; + private static readonly CanceledExceptionInternal s_instance = new CanceledExceptionInternal("Operation was canceled."); + + internal static CanceledExceptionInternal GetOrCreate() => s_instance; #endif - } + private CanceledExceptionInternal(string message) : base(message) { } } @@ -75,7 +72,7 @@ internal RejectionException(string message, string stackTrace, Exception innerEx _stackTrace = stackTrace; } - public override string StackTrace { get { return _stackTrace; } } + public override string StackTrace => _stackTrace; } #if !PROTO_PROMISE_DEVELOPER_MODE @@ -91,14 +88,10 @@ internal RejectExceptionInternal(object value) } IRejectContainer IRejectionToContainer.ToContainer(ITraceable traceable) - { - return CreateRejectContainer(_value, int.MinValue, this, traceable); - } + => CreateRejectContainer(_value, int.MinValue, this, traceable); void ICantHandleException.ReportUnhandled(ITraceable traceable) - { - ReportRejection(_value, traceable); - } + => ReportRejection(_value, traceable); } } } \ No newline at end of file diff --git a/Package/Core/InternalShared/HelperFunctionsInternal.cs b/Package/Core/InternalShared/HelperFunctionsInternal.cs index c5b35f3c..5fd62d3c 100644 --- a/Package/Core/InternalShared/HelperFunctionsInternal.cs +++ b/Package/Core/InternalShared/HelperFunctionsInternal.cs @@ -238,5 +238,26 @@ internal static CancelationSource MaybeJoinCancelationTokens(CancelationToken fi maybeJoinedToken = source.Token; return source; } + + internal static void SetOrAdd(this IList list, in T value, int index) + { + if (index < list.Count) + { + list[index] = value; + } + else + { + list.Add(value); + } + } + + internal static void MaybeShrink(this IList list, int expectedCount) + { + int listCount = list.Count; + while (listCount > expectedCount) + { + list.RemoveAt(--listCount); + } + } } // class Internal } // namespace Proto.Promises \ No newline at end of file diff --git a/Package/Core/InternalShared/InterfacesInternal.cs b/Package/Core/InternalShared/InterfacesInternal.cs index 11b04788..9f6d8fa0 100644 --- a/Package/Core/InternalShared/InterfacesInternal.cs +++ b/Package/Core/InternalShared/InterfacesInternal.cs @@ -1,4 +1,5 @@ -using System.Runtime.ExceptionServices; +using System; +using System.Runtime.ExceptionServices; namespace Proto.Promises { @@ -23,9 +24,10 @@ internal interface ICantHandleException internal interface IRejectContainer { + object Value { get; } + Exception GetValueAsException(); void ReportUnhandled(); ExceptionDispatchInfo GetExceptionDispatchInfo(); - object Value { get; } } internal interface INullable diff --git a/Package/Core/InternalShared/ValueCollectionsInternal.cs b/Package/Core/InternalShared/ValueCollectionsInternal.cs index 117fa555..744db328 100644 --- a/Package/Core/InternalShared/ValueCollectionsInternal.cs +++ b/Package/Core/InternalShared/ValueCollectionsInternal.cs @@ -162,6 +162,24 @@ internal T Pop() MarkRemovedFromCollection(temp); return temp; } + + [MethodImpl(InlineOption)] + internal void PushInterlocked(T item) + { + AssertNotInCollection(item); + + var head = _head; + while (true) + { + item.Next = head; + var oldHead = Interlocked.CompareExchange(ref _head, item, head); + if (oldHead == head) + { + break; + } + head = oldHead; + } + } } /// diff --git a/Package/Core/Linq/CompilerServices/AsyncIteratorMethodBuilder.cs b/Package/Core/Linq/CompilerServices/AsyncIteratorMethodBuilder.cs index b3ff9df4..78137cc8 100644 --- a/Package/Core/Linq/CompilerServices/AsyncIteratorMethodBuilder.cs +++ b/Package/Core/Linq/CompilerServices/AsyncIteratorMethodBuilder.cs @@ -45,7 +45,7 @@ public static AsyncIteratorMethodBuilder Create() /// The to use to reject the promise. public void SetException(Exception exception) { - if (exception == AsyncEnumerableDisposedException.s_instance) + if (AsyncEnumerableDisposedException.Is(exception)) { // The await foreach loop was stopped with a `break`. SetResult(); diff --git a/Package/Core/Linq/Internal/AsyncEnumerableDisposedException.cs b/Package/Core/Linq/Internal/AsyncEnumerableDisposedException.cs index 30d87b8d..156ea909 100644 --- a/Package/Core/Linq/Internal/AsyncEnumerableDisposedException.cs +++ b/Package/Core/Linq/Internal/AsyncEnumerableDisposedException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.CompilerServices; #pragma warning disable IDE0090 // Use 'new(...)' @@ -6,9 +7,26 @@ namespace Proto.Promises.CompilerServices { internal sealed class AsyncEnumerableDisposedException : Exception { + // Old Unity runtime has a bug where stack traces are continually appended to the exception, causing a memory leak and runtime slowdowns. + // To avoid the issue, we only use a singleton in runtimes where the bug is not present. +#if NETSTANDARD2_0 || (UNITY_2018_3_OR_NEWER && !UNITY_2021_2_OR_NEWER) + [MethodImpl(Internal.InlineOption)] + internal static AsyncEnumerableDisposedException GetOrCreate() => new AsyncEnumerableDisposedException(); + + [MethodImpl(Internal.InlineOption)] + internal static bool Is(Exception exception) => exception is AsyncEnumerableDisposedException; +#else // We can use a singleton instance since we never care about the stack trace. - internal static readonly AsyncEnumerableDisposedException s_instance = new AsyncEnumerableDisposedException(); + private static readonly AsyncEnumerableDisposedException s_instance = new AsyncEnumerableDisposedException(); + + [MethodImpl(Internal.InlineOption)] + internal static AsyncEnumerableDisposedException GetOrCreate() => s_instance; + + [MethodImpl(Internal.InlineOption)] + internal static bool Is(Exception exception) => exception == s_instance; +#endif private AsyncEnumerableDisposedException() : base("This is a special exception used for async enumerables. It should never be caught by user code!") { } + } } \ No newline at end of file diff --git a/Package/Core/Linq/Internal/AsyncEnumerableInternal.cs b/Package/Core/Linq/Internal/AsyncEnumerableInternal.cs index 2b1451af..71363da3 100644 --- a/Package/Core/Linq/Internal/AsyncEnumerableInternal.cs +++ b/Package/Core/Linq/Internal/AsyncEnumerableInternal.cs @@ -170,6 +170,14 @@ protected void SetStateForDisposeWithoutStart() // This is never used as a backing reference for Promises, so we need to suppress the UnobservedPromiseException from the base finalizer. WasAwaitedOrForgotten = true; } + + protected void ResetForNextAwait() + { + // Invalidate the previous awaiter. + IncrementPromiseIdAndClearPrevious(); + // Reset for the next awaiter. + ResetWithoutStacktrace(); + } } // class AsyncEnumerableBase #if !PROTO_PROMISE_DEVELOPER_MODE @@ -219,10 +227,7 @@ internal override Promise MoveNextAsync(int id) private void MoveNext() { - // Invalidate the previous awaiter. - IncrementPromiseIdAndClearPrevious(); - // Reset for the next awaiter. - ResetWithoutStacktrace(); + ResetForNextAwait(); // Handle iterator promise to move the async state machine forward. Interlocked.Exchange(ref _iteratorPromiseRef, null).Handle(this, Promise.State.Resolved); } @@ -285,15 +290,12 @@ internal override Promise DisposeAsync(int id) _current = default; _iteratorCompleteExpectedId = newId; _iteratorCompleteId = newId; - // Invalidate the previous awaiter. - IncrementPromiseIdAndClearPrevious(); - // Reset for the next awaiter. - ResetWithoutStacktrace(); + ResetForNextAwait(); iteratorPromise.Handle(this, Promise.State.Resolved); return new Promise(this, Id); } - internal override void Handle(PromiseRefBase handler, Promise.State state) + internal override sealed void Handle(PromiseRefBase handler, Promise.State state) { // This is called when the async iterator function completes. ThrowIfInPool(this); @@ -338,7 +340,7 @@ internal void GetResultForAsyncStreamYielder(int enumerableId) // Reset in case the async iterator function completes synchronously from Start. ResetWithoutStacktrace(); // Throw this special exception so that the async iterator function will run any finally blocks and complete. - throw AsyncEnumerableDisposedException.s_instance; + throw AsyncEnumerableDisposedException.GetOrCreate(); } throw new InvalidOperationException("AsyncStreamYielder.GetResult: instance is not valid. This should only be called from the async iterator method, and it may only be called once.", GetFormattedStacktrace(2)); } @@ -419,10 +421,7 @@ protected override void Start(int enumerableId) return; } - // We only set _previous to support circular await detection. -#if PROMISE_DEBUG - _previous = iteratorPromise._ref; -#endif + this.SetPrevious(iteratorPromise._ref); // We hook this up directly to the returned promise so we can know when the iteration is complete, and use this for the DisposeAsync promise. iteratorPromise._ref.HookupExistingWaiter(iteratorPromise._id, this); } diff --git a/Package/Core/Linq/Internal/AsyncEnumerablePartitionInternal.cs b/Package/Core/Linq/Internal/AsyncEnumerablePartitionInternal.cs index cafcd427..06a0eb2b 100644 --- a/Package/Core/Linq/Internal/AsyncEnumerablePartitionInternal.cs +++ b/Package/Core/Linq/Internal/AsyncEnumerablePartitionInternal.cs @@ -93,10 +93,7 @@ protected override void Start(int enumerableId) return; } - // We only set _previous to support circular await detection. -#if PROMISE_DEBUG - _previous = iteratorPromise._ref; -#endif + this.SetPrevious(iteratorPromise._ref); // We hook this up directly to the returned promise so we can know when the iteration is complete, and use this for the DisposeAsync promise. iteratorPromise._ref.HookupExistingWaiter(iteratorPromise._id, this); } @@ -314,10 +311,7 @@ protected override void Start(int enumerableId) return; } - // We only set _previous to support circular await detection. -#if PROMISE_DEBUG - _previous = iteratorPromise._ref; -#endif + this.SetPrevious(iteratorPromise._ref); // We hook this up directly to the returned promise so we can know when the iteration is complete, and use this for the DisposeAsync promise. iteratorPromise._ref.HookupExistingWaiter(iteratorPromise._id, this); } diff --git a/Package/Core/Linq/Internal/MergeInternal.cs b/Package/Core/Linq/Internal/MergeInternal.cs index 0ac79190..f88e0906 100644 --- a/Package/Core/Linq/Internal/MergeInternal.cs +++ b/Package/Core/Linq/Internal/MergeInternal.cs @@ -21,10 +21,10 @@ partial class Internal #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif - internal abstract class AsyncEnumerableMergerBase : PromiseRefBase.AsyncEnumerableWithIterator + internal abstract partial class AsyncEnumerableMergerBase : PromiseRefBase.AsyncEnumerableWithIterator { // These must not be readonly. - // We queue the successful MoveNextAsync results instead of using Promise.RaceWithIndex, to avoid having to preserve each promise. + // We queue the successful MoveNextAsync results instead of using Promise.RaceWithIndex, to avoid having to retain each promise. protected SingleConsumerAsyncQueueInternal _readyQueue; protected TempCollectionBuilder> _enumerators; protected TempCollectionBuilder<(IRejectContainer rejectContainer, Promise disposePromise)> _disposePromises; @@ -51,6 +51,7 @@ protected void ContinueMerge(int index) else { // The promise may still be pending, hook this up to continue when it completes. + AddPending(moveNextPromise._ref); var passthrough = PromisePassThrough.GetOrCreate(moveNextPromise._ref, this, index); moveNextPromise._ref.HookupNewWaiter(moveNextPromise._id, passthrough); return; @@ -68,6 +69,7 @@ protected void ContinueMerge(int index) internal override void Handle(PromiseRefBase handler, Promise.State state, int index) { + RemoveComplete(handler); handler.SetCompletionState(state); bool hasValue = state == Promise.State.Resolved & handler.GetResult(); if (hasValue) @@ -120,11 +122,61 @@ protected void RecordException(Exception e) [MethodImpl(InlineOption)] new protected void Dispose() { + ValidateNoPending(); base.Dispose(); _exceptions = null; } + + partial void AddPending(PromiseRefBase pendingPromise); + partial void RemoveComplete(PromiseRefBase completePromise); + partial void ValidateNoPending(); } // class AsyncEnumerableMergerBase +#if PROMISE_DEBUG + partial class AsyncEnumerableMergerBase + { + private readonly HashSet _pendingPromises = new HashSet(); + + protected override void BorrowPreviousPromises(Stack borrower) + { + lock (_pendingPromises) + { + foreach (var promiseRef in _pendingPromises) + { + borrower.Push(promiseRef); + } + } + } + + partial void ValidateNoPending() + { + lock (_pendingPromises) + { + if (_pendingPromises.Count != 0) + { + throw new System.InvalidOperationException("AsyncEnumerableMerger disposed with pending promises."); + } + } + } + + partial void AddPending(PromiseRefBase pendingPromise) + { + lock (_pendingPromises) + { + _pendingPromises.Add(pendingPromise); + } + } + + partial void RemoveComplete(PromiseRefBase completePromise) + { + lock (_pendingPromises) + { + _pendingPromises.Remove(completePromise); + } + } + } +#endif // PROMISE_DEBUG + #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif @@ -223,10 +275,7 @@ protected override void Start(int enumerableId) return; } - // We only set _previous to support circular await detection. -#if PROMISE_DEBUG - _previous = iteratorPromise._ref; -#endif + this.SetPrevious(iteratorPromise._ref); // We hook this up directly to the returned promise so we can know when the iteration is complete, and use this for the DisposeAsync promise. iteratorPromise._ref.HookupExistingWaiter(iteratorPromise._id, this); } @@ -314,11 +363,7 @@ private async AsyncIteratorMethod Iterate(int streamWriterId) } if (rejectContainer != null) { - var container = rejectContainer; - var exception = container.Value as Exception - // If the reason was not an exception, get the reason wrapped in an exception. - ?? container.GetExceptionDispatchInfo().SourceException; - RecordException(exception); + RecordException(rejectContainer.GetValueAsException()); } } @@ -466,10 +511,7 @@ protected override void Start(int enumerableId) return; } - // We only set _previous to support circular await detection. -#if PROMISE_DEBUG - _previous = iteratorPromise._ref; -#endif + this.SetPrevious(iteratorPromise._ref); // We hook this up directly to the returned promise so we can know when the iteration is complete, and use this for the DisposeAsync promise. iteratorPromise._ref.HookupExistingWaiter(iteratorPromise._id, this); } @@ -550,11 +592,7 @@ private async AsyncIteratorMethod Iterate(int streamWriterId) } if (rejectContainer != null) { - var container = rejectContainer; - var exception = container.Value as Exception - // If the reason was not an exception, get the reason wrapped in an exception. - ?? container.GetExceptionDispatchInfo().SourceException; - RecordException(exception); + RecordException(rejectContainer.GetValueAsException()); } } diff --git a/Package/Core/PromiseGroups.meta b/Package/Core/PromiseGroups.meta new file mode 100644 index 00000000..213d7ae4 --- /dev/null +++ b/Package/Core/PromiseGroups.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b2008aa79c79fed43a1328ab5b55062d +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal.meta b/Package/Core/PromiseGroups/Internal.meta new file mode 100644 index 00000000..d790c4b1 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 65adde5286e83554a8920e62c333a7b2 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs new file mode 100644 index 00000000..42c51ab0 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs @@ -0,0 +1,112 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class AllPromiseGroup : MergePromiseGroupBase> + { + [MethodImpl(InlineOption)] + private static AllPromiseGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new AllPromiseGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static AllPromiseGroup GetOrCreate(CancelationRef cancelationSource, IList value) + { + var promise = GetOrCreate(); + promise._result = value; + promise._completeState = Promise.State.Resolved; // Default to Resolved state. If the promise is actually canceled or rejected, the state will be overwritten. + promise.Reset(cancelationSource); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromisePassThroughForMergeGroup passthrough, PromiseRefBase handler, Promise.State state) + { + // We store the passthrough until all promises are complete, + // so that items won't be written to the list while it's being expanded on another thread. + RemovePromiseAndSetCompletionState(handler, state); + _completedPassThroughs.PushInterlocked(passthrough); + if (state != Promise.State.Resolved) + { + CancelGroup(); + } + if (TryComplete()) + { + // All promises are complete. + HandleNextInternal(CompleteAndGetState()); + } + } + + private Promise.State CompleteAndGetState() + { + var state = _completeState; + var passthroughs = _completedPassThroughs.TakeAndClear(); + while (passthroughs.IsNotEmpty) + { + var passthrough = passthroughs.Pop(); + var owner = passthrough.Owner; + _result[passthrough.Index] = owner.GetResult(); + if (owner.State == Promise.State.Rejected) + { + RecordException(owner._rejectContainer.GetValueAsException()); + } + passthrough.Dispose(); + } + + if (_exceptions != null) + { + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(_exceptions), int.MinValue, null, this); + _exceptions = null; + } + + return state; + } + + internal void MarkReady(int totalPromises) + { + // This method is called after all promises have been hooked up to this. + if (MarkReadyAndGetIsComplete(totalPromises)) + { + // All promises already completed. + _next = PromiseCompletionSentinel.s_instance; + SetCompletionState(CompleteAndGetState()); + } + } + } + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.AllPromiseGroup GetOrCreateAllPromiseGroup(CancelationRef cancelationSource, IList value) + => PromiseRefBase.AllPromiseGroup.GetOrCreate(cancelationSource, value); + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ThrowInvalidAllGroup(int skipFrames) + => throw new InvalidOperationException("The promise all group is invalid.", GetFormattedStacktrace(skipFrames + 1)); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs.meta new file mode 100644 index 00000000..398bac74 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseAllGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b6da9353f23b1eb46b00bcff9bf63679 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs new file mode 100644 index 00000000..7b80bd02 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs @@ -0,0 +1,193 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class AllPromiseResultsGroupVoid : MergePromiseGroupBase> + { + [MethodImpl(InlineOption)] + private static AllPromiseResultsGroupVoid GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid(); + return obj == InvalidAwaitSentinel.s_instance + ? new AllPromiseResultsGroupVoid() + : obj.UnsafeAs(); + } + + [MethodImpl(InlineOption)] + internal static AllPromiseResultsGroupVoid GetOrCreate(CancelationRef cancelationSource, IList value) + { + var promise = GetOrCreate(); + promise._result = value; + promise._completeState = Promise.State.Resolved; // Default to Resolved state. If the promise is actually canceled or rejected, the state will be overwritten. + promise.Reset(cancelationSource); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromisePassThroughForMergeGroup passthrough, PromiseRefBase handler, Promise.State state) + { + // We store the passthrough until all promises are complete, + // so that items won't be written to the list while it's being expanded on another thread. + RemovePromiseAndSetCompletionState(handler, state); + _completedPassThroughs.PushInterlocked(passthrough); + if (state != Promise.State.Resolved) + { + CancelGroup(); + } + if (TryComplete()) + { + // All promises are complete. + HandleNextInternal(CompleteAndGetState()); + } + } + + private Promise.State CompleteAndGetState() + { + // If any of the promises in the group completed unsuccessfully, the group state was set to canceled. + // We ignore that and set it to always resolved, because we're yielding ResultContainers. + var state = Promise.State.Resolved; + if (_exceptions != null) + { + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(_exceptions), int.MinValue, null, this); + _exceptions = null; + } + + var passthroughs = _completedPassThroughs.TakeAndClear(); + while (passthroughs.IsNotEmpty) + { + var passthrough = passthroughs.Pop(); + var owner = passthrough.Owner; + _result[passthrough.Index] = new Promise.ResultContainer(owner._rejectContainer, owner.State); + passthrough.Dispose(); + } + + return state; + } + + internal void MarkReady(int totalPromises) + { + // This method is called after all promises have been hooked up to this. + if (MarkReadyAndGetIsComplete(totalPromises)) + { + // All promises already completed. + _next = PromiseCompletionSentinel.s_instance; + SetCompletionState(CompleteAndGetState()); + } + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class AllPromiseResultsGroup : MergePromiseGroupBase.ResultContainer>> + { + [MethodImpl(InlineOption)] + private static AllPromiseResultsGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new AllPromiseResultsGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static AllPromiseResultsGroup GetOrCreate(CancelationRef cancelationSource, IList.ResultContainer> value) + { + var promise = GetOrCreate(); + promise._result = value; + promise._completeState = Promise.State.Resolved; // Default to Resolved state. If the promise is actually canceled or rejected, the state will be overwritten. + promise.Reset(cancelationSource); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromisePassThroughForMergeGroup passthrough, PromiseRefBase handler, Promise.State state) + { + // We store the passthrough until all promises are complete, + // so that items won't be written to the list while it's being expanded on another thread. + RemovePromiseAndSetCompletionState(handler, state); + _completedPassThroughs.PushInterlocked(passthrough); + if (state != Promise.State.Resolved) + { + CancelGroup(); + } + if (TryComplete()) + { + // All promises are complete. + HandleNextInternal(CompleteAndGetState()); + } + } + + private Promise.State CompleteAndGetState() + { + // If any of the promises in the group completed unsuccessfully, the group state was set to canceled. + // We ignore that and set it to always resolved, because we're yielding ResultContainers. + var state = Promise.State.Resolved; + if (_exceptions != null) + { + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(_exceptions), int.MinValue, null, this); + _exceptions = null; + } + + var passthroughs = _completedPassThroughs.TakeAndClear(); + while (passthroughs.IsNotEmpty) + { + var passthrough = passthroughs.Pop(); + var owner = passthrough.Owner; + _result[passthrough.Index] = new Promise.ResultContainer(owner.GetResult(), owner._rejectContainer, owner.State); + passthrough.Dispose(); + } + + return state; + } + + internal void MarkReady(int totalPromises) + { + // This method is called after all promises have been hooked up to this. + if (MarkReadyAndGetIsComplete(totalPromises)) + { + // All promises already completed. + _next = PromiseCompletionSentinel.s_instance; + SetCompletionState(CompleteAndGetState()); + } + } + } + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.AllPromiseResultsGroupVoid GetOrCreateAllPromiseResultsGroup(CancelationRef cancelationSource, IList value) + => PromiseRefBase.AllPromiseResultsGroupVoid.GetOrCreate(cancelationSource, value); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.AllPromiseResultsGroup GetOrCreateAllPromiseResultsGroup(CancelationRef cancelationSource, IList.ResultContainer> value) + => PromiseRefBase.AllPromiseResultsGroup.GetOrCreate(cancelationSource, value); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs.meta new file mode 100644 index 00000000..d2272f9b --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseAllResultsGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 692b5d70ae8db7e4086c1bb8f7923522 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs new file mode 100644 index 00000000..e05fa152 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs @@ -0,0 +1,363 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class EachPromiseGroup : AsyncEnumerableBase, ICancelable + where TResult : IResultContainer + { + private static GetResultDelegate s_getResult; + + private CancelationRef _cancelationRef; // Store the reference directly instead of CancelationSource struct to reduce memory. + private Exception _cancelationException; // In case a cancelation token callback throws, we have to store it to rethrow it from DisposeAsync. + private CancelationRegistration _cancelationRegistration; + // This must not be readonly. + private PoolBackedQueue _queue; + private int _remaining; + private int _retainCount; + private bool _isMoveNextAsyncPending; + private bool _isIterationCanceled; + private bool _suppressUnobservedRejections; + + private EachPromiseGroup() { } + + [MethodImpl(InlineOption)] + private static EachPromiseGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new EachPromiseGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static EachPromiseGroup GetOrCreate(CancelationRef cancelationRef, GetResultDelegate getResultDelegate) + { + s_getResult = getResultDelegate; + + var enumerable = GetOrCreate(); + enumerable.Reset(); + enumerable._queue = new PoolBackedQueue(0); + enumerable._cancelationRef = cancelationRef; + enumerable._isIterationCanceled = false; + return enumerable; + } + + [MethodImpl(InlineOption)] + internal bool TryIncrementId(int id) + => Interlocked.CompareExchange(ref _enumerableId, unchecked(id + 1), id) == id; + + [MethodImpl(InlineOption)] + internal void AddResult(in TResult result) + { + lock (this) + { + _queue.Enqueue(result); + } + } + + internal void AddPromise(PromiseRefBase promise, short id) + { + AddPending(promise); + promise.HookupNewWaiter(id, this); + } + + internal void MarkReady(bool suppressUnobservedRejections, int pendingCount, int totalCount) + { + // This method is called after all promises have been hooked up to this. + _suppressUnobservedRejections = suppressUnobservedRejections; + _remaining = totalCount; + // _retainCount starts at 0 and is decremented every time an added promise completes. + // We add back the number of pending promises that were added, plus 1 extra retain for DisposeAsync, + // and when the count goes back to 0, this is complete. + Interlocked.Add(ref _retainCount, unchecked(pendingCount + 1)); + } + + private void CancelGroup() + { + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + _cancelationException = e; + } + } + + new private void Dispose() + { + ValidateNoPending(); + +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE + SetCompletionState(Promise.State.Resolved); +#endif + // MoveNextAsync/DisposeAsync may have completed synchronously, in which case this will never have had a waiter added to it. + // So we need to mark it awaited to prevent the finalizer from reporting it as not awaited. + WasAwaitedOrForgotten = true; + base.Dispose(); + _current = default; + _cancelationException = null; + _cancelationRef.TryDispose(_cancelationRef.SourceId); + _cancelationRef = null; + _queue.Dispose(); + _queue = default; + ObjectPool.MaybeRepool(this); + } + + internal override Promise DisposeAsync(int id) + { + int newId = id + 2; + int oldId = Interlocked.CompareExchange(ref _enumerableId, newId, id); + if (oldId != id) + { + if (oldId == id + 1) + { + throw new InvalidOperationException("AsyncEnumerator.DisposeAsync: the previous MoveNextAsync operation is still pending.", GetFormattedStacktrace(2)); + } + // IAsyncDisposable.DisposeAsync must not throw if it's called multiple times, according to MSDN documentation. + return Promise.Resolved(); + } + + _disposed = true; + CancelGroup(); + _cancelationRegistration.Dispose(); + _cancelationRegistration = default; + + // We have to reset this before decrementing _retainCount, or else MaybeHandleDisposeAsync could be called on another thread, + // causing the async state machine to be moved forward too early. + ResetForNextAwait(); + + if (InterlockedAddWithUnsignedOverflowCheck(ref _retainCount, -1) != 0) + { + return new Promise(this, Id); + } + + var exception = GetAggregateException(); + Dispose(); + return exception == null + ? Promise.Resolved() + : Promise.Rejected(exception); + } + + private AggregateException GetAggregateException() + { + // If a cancelation token callback threw, we always propagate it, regardless of the _suppressUnobservedRejections flag. + List exceptions = _cancelationException == null + ? null + : new List() { _cancelationException }; + if (!_suppressUnobservedRejections) + { + while (_queue.TryDequeue(out var result)) + { + var rejectContainer = result.RejectContainer; + if (rejectContainer != null) + { + RecordException(rejectContainer.GetValueAsException(), ref exceptions); + } + } + } + return exceptions == null + ? null + : new AggregateException(exceptions); + } + + internal override void MaybeDispose() + { + // This is called on every MoveNextAsync, we only fully dispose and return to pool after DisposeAsync is called. + if (_disposed) + { + Dispose(); + } + } + + internal override Promise MoveNextAsync(int id) + { + if (Interlocked.CompareExchange(ref _enumerableId, id + 1, id) != id) + { + throw new InvalidOperationException("AsyncEnumerable.MoveNextAsync: instance is not valid, or the previous MoveNextAsync operation is still pending.", GetFormattedStacktrace(2)); + } + + if (!_isStarted) + { + _isStarted = true; + _cancelationToken.TryRegisterWithoutImmediateInvoke(this, out _cancelationRegistration, out bool alreadyCanceled); + if (alreadyCanceled) + { + _enumerableId = id; + return Promise.Canceled(); + } + } + else if (_remaining == 0) + { + _enumerableId = id; + return Promise.Resolved(false); + } + else if (_cancelationToken.IsCancelationRequested | _isIterationCanceled) + { + _enumerableId = id; + return Promise.Canceled(); + } + --_remaining; + + // Reset this before entering the lock so that we're not spending extra time inside the lock. + ResetForNextAwait(); + bool pending; + lock (this) + { + _isMoveNextAsyncPending = pending = !_queue.TryDequeue(out _current); + } + if (pending) + { + return new Promise(this, Id); + } + _enumerableId = id; + return Promise.Resolved(true); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + RemoveComplete(handler); + + handler.SetCompletionState(state); + TResult result = default; + s_getResult.Invoke(handler, 0, ref result); + handler.MaybeDispose(); + + lock (this) + { + if (_isMoveNextAsyncPending) + { + // MoveNextAsync is pending, so we can skip the queue and just set the current directly. + _isMoveNextAsyncPending = false; + goto HandleNext; + } + _queue.Enqueue(result); + } + MaybeHandleDisposeAsync(); + return; + + HandleNext: + --_enumerableId; + _result = true; + _current = result; + Interlocked.Decrement(ref _retainCount); + HandleNextInternal(Promise.State.Resolved); + } + + private void MaybeHandleDisposeAsync() + { + if (Interlocked.Decrement(ref _retainCount) != 0) + { + return; + } + + var exception = GetAggregateException(); + if (exception == null) + { + HandleNextInternal(Promise.State.Resolved); + return; + } + + _rejectContainer = CreateRejectContainer(exception, int.MinValue, null, this); + HandleNextInternal(Promise.State.Rejected); + } + + void ICancelable.Cancel() + { + _isIterationCanceled = true; + CancelGroup(); + + lock (this) + { + if (!_isMoveNextAsyncPending) + { + return; + } + _isMoveNextAsyncPending = false; + } + --_enumerableId; + _result = false; + HandleNextInternal(Promise.State.Canceled); + } + + partial void AddPending(PromiseRefBase pendingPromise); + partial void RemoveComplete(PromiseRefBase completePromise); + partial void ValidateNoPending(); + } + +#if PROMISE_DEBUG + partial class EachPromiseGroup + { + private readonly HashSet _pendingPromises = new HashSet(); + + protected override void BorrowPreviousPromises(Stack borrower) + { + lock (_pendingPromises) + { + foreach (var promiseRef in _pendingPromises) + { + borrower.Push(promiseRef); + } + } + } + + partial void ValidateNoPending() + { + lock (_pendingPromises) + { + if (_pendingPromises.Count != 0) + { + throw new System.InvalidOperationException("PromiseEachAsyncEnumerable disposed with pending promises."); + } + } + } + + partial void AddPending(PromiseRefBase pendingPromise) + { + lock (_pendingPromises) + { + _pendingPromises.Add(pendingPromise); + } + } + + partial void RemoveComplete(PromiseRefBase completePromise) + { + lock (_pendingPromises) + { + _pendingPromises.Remove(completePromise); + } + } + } +#endif // PROMISE_DEBUG + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.EachPromiseGroup.ResultContainer> GetOrCreateEachPromiseGroup(CancelationRef cancelationRef) + => PromiseRefBase.EachPromiseGroup.ResultContainer>.GetOrCreate(cancelationRef, Promise.MergeResultFuncs.GetMergeResult()); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.EachPromiseGroup GetOrCreateEachPromiseGroup(CancelationRef cancelationRef) + => PromiseRefBase.EachPromiseGroup.GetOrCreate(cancelationRef, Promise.MergeResultFuncs.GetMergeResultVoid()); + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ThrowInvalidEachGroup(int skipFrames) + => throw new InvalidOperationException("The promise each group is invalid.", GetFormattedStacktrace(skipFrames + 1)); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs.meta new file mode 100644 index 00000000..b9a373f0 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseEachGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4f72f0dbe66befa4aa9f35f0c078fc4e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs new file mode 100644 index 00000000..66b4a32d --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs @@ -0,0 +1,172 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +#pragma warning disable IDE0090 // Use 'new(...)' + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal abstract partial class PromiseGroupBase : PromiseSingleAwait + { + partial void AddPending(PromiseRefBase pendingPromise); + partial void RemoveComplete(PromiseRefBase completePromise); + partial void ValidateNoPending(); + + internal override void Handle(PromiseRefBase handler, Promise.State state) { throw new System.InvalidOperationException(); } + + [MethodImpl(InlineOption)] + protected void Reset(CancelationRef cancelationSource) + { + _cancelationRef = cancelationSource; + _cancelationId = cancelationSource.SourceId; + Reset(); + } + + new protected void Dispose() + { + ValidateNoPending(); + base.Dispose(); + _cancelationRef.TryDispose(_cancelationId); + _cancelationRef = null; + } + + [MethodImpl(InlineOption)] + protected bool TryComplete() + // We don't do an overflow check here, because it starts at zero + // and a promise may complete and decrement it before MarkReady() is called. + => Interlocked.Decrement(ref _waitCount) == 0; + + [MethodImpl(InlineOption)] + protected void RemovePromiseAndSetCompletionState(PromiseRefBase completePromise, Promise.State state) + { + RemoveComplete(completePromise); + completePromise.SetCompletionState(state); + } + + [MethodImpl(InlineOption)] + internal void AddPromiseWithIndex(PromiseRefBase promise, short id, int index) + { + AddPending(promise); + var passthrough = PromisePassThrough.GetOrCreate(promise, this, index); + promise.HookupNewWaiter(id, passthrough); + } + + [MethodImpl(InlineOption)] + internal void AddPromiseForMerge(PromiseRefBase promise, short id, int index) + { + AddPending(promise); + var passthrough = PromisePassThroughForMergeGroup.GetOrCreate(promise, this, index); + promise.HookupNewWaiter(id, passthrough); + } + + [MethodImpl(InlineOption)] + internal void AddPromise(PromiseRefBase promise, short id) + { + AddPending(promise); + promise.HookupNewWaiter(id, this); + } + + [MethodImpl(InlineOption)] + internal bool TryIncrementId(short id) + { + // Unfortunately Interlocked doesn't contain APIs for short prior to .Net 9, so this is not thread-safe. + // But it's just a light check which users shouldn't be mis-using anyway, so it's not a big deal. + // It's probably not worth adding conditional compilation to use Interlocked in .Net 9. + if (id != _promiseId) + { + return false; + } + IncrementPromiseId(); + return true; + } + + [MethodImpl(InlineOption)] + protected bool MarkReadyAndGetIsComplete(int totalPromises) + // _waitCount starts at 0 and is decremented every time an added promise completes. + // We add back the number of promises that were added, and when the count goes back to 0, all promises are complete. + => Interlocked.Add(ref _waitCount, totalPromises) == 0; + + protected void CancelGroup() + { + // This may be called multiple times. It's fine because it checks internally if it's already canceled. + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + RecordException(e); + } + } + + internal void RecordException(Exception e) + { + lock (this) + { + Internal.RecordException(e, ref _exceptions); + } + } + } + +#if PROMISE_DEBUG + partial class PromiseGroupBase + { + private readonly HashSet _pendingPromises = new HashSet(); + + protected override void BorrowPreviousPromises(Stack borrower) + { + lock (_pendingPromises) + { + foreach (var promiseRef in _pendingPromises) + { + borrower.Push(promiseRef); + } + } + } + + partial void ValidateNoPending() + { + lock (_pendingPromises) + { + if (_pendingPromises.Count != 0) + { + throw new System.InvalidOperationException("PromiseGroupBase disposed with pending promises."); + } + } + } + + partial void AddPending(PromiseRefBase pendingPromise) + { + lock (_pendingPromises) + { + _pendingPromises.Add(pendingPromise); + } + } + + partial void RemoveComplete(PromiseRefBase completePromise) + { + lock (_pendingPromises) + { + _pendingPromises.Remove(completePromise); + } + } + } +#endif + } // class PromiseRefBase + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs.meta new file mode 100644 index 00000000..853fa342 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseGroupBaseInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e1f02df80e1de70468f44042fda1632e +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs new file mode 100644 index 00000000..170e6706 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs @@ -0,0 +1,289 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Proto.Promises +{ + partial class Internal + { + partial class HandleablePromiseBase + { + internal virtual void Handle(PromiseRefBase.PromisePassThroughForMergeGroup passthrough, PromiseRefBase handler, Promise.State state) { throw new System.InvalidOperationException(); } + } + + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class PromisePassThroughForMergeGroup : PromisePassThrough, ILinked + { + PromisePassThroughForMergeGroup ILinked.Next + { + get => _next.UnsafeAs(); + set => _next = value; + } + + internal PromiseRefBase Owner + { + [MethodImpl(InlineOption)] + get => _owner; + } + + internal int Index + { + [MethodImpl(InlineOption)] + get => _index; + } + + private PromisePassThroughForMergeGroup() { } + + [MethodImpl(InlineOption)] + private static PromisePassThroughForMergeGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid(); + return obj == InvalidAwaitSentinel.s_instance + ? new PromisePassThroughForMergeGroup() + : obj.UnsafeAs(); + } + + [MethodImpl(InlineOption)] + new internal static PromisePassThroughForMergeGroup GetOrCreate(PromiseRefBase owner, PromiseRefBase target, int index) + { + var passThrough = GetOrCreate(); + passThrough._next = target; + passThrough._index = index; + passThrough._owner = owner; +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE + passThrough._disposed = false; +#endif + return passThrough; + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + // Unlike regular PromisePassThrough, we don't dispose here. This gets stored in the merge group promise, + // so that the ultimate ValueTuple result will be filled with the proper type, and will be disposed when it's complete. + => _next.Handle(this, handler, state); + + internal void Dispose() + { + ThrowIfInPool(this); + _owner.MaybeDispose(); + _owner = null; +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE + _disposed = true; +#endif + ObjectPool.MaybeRepool(this); + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal abstract partial class MergePromiseGroupBase : PromiseGroupBase + { + [MethodImpl(InlineOption)] + new protected void CancelGroup() + { + _completeState = Promise.State.Canceled; + base.CancelGroup(); + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class MergePromiseGroupVoid : MergePromiseGroupBase + { + [MethodImpl(InlineOption)] + private static MergePromiseGroupVoid GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid(); + return obj == InvalidAwaitSentinel.s_instance + ? new MergePromiseGroupVoid() + : obj.UnsafeAs(); + } + + [MethodImpl(InlineOption)] + internal static MergePromiseGroupVoid GetOrCreate(CancelationRef cancelationSource) + { + var promise = GetOrCreate(); + promise._completeState = Promise.State.Resolved; // Default to Resolved state. If the promise is actually canceled or rejected, the state will be overwritten. + promise.Reset(cancelationSource); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + // This is called from void promises. They don't need to update any value, so they have no index. + RemovePromiseAndSetCompletionState(handler, state); + if (state != Promise.State.Resolved) + { + CancelGroup(); + if (state == Promise.State.Rejected) + { + RecordException(handler._rejectContainer.GetValueAsException()); + } + } + handler.MaybeDispose(); + if (TryComplete()) + { + // All promises are complete. + HandleNextInternal(CompleteAndGetState()); + } + } + + private Promise.State CompleteAndGetState() + { + if (_exceptions == null) + { + return _completeState; + } + + _rejectContainer = CreateRejectContainer(new AggregateException(_exceptions), int.MinValue, null, this); + _exceptions = null; + return Promise.State.Rejected; + } + + internal override void Handle(PromisePassThroughForMergeGroup passthrough, PromiseRefBase handler, Promise.State state) + { + // We store the passthrough until all promises are complete, + // so that the ultimate ValueTuple will be filled with the proper types. + // We don't handle the rejection here, it is handled in the attached promise. + RemovePromiseAndSetCompletionState(handler, state); + _completedPassThroughs.PushInterlocked(passthrough); + if (state != Promise.State.Resolved) + { + CancelGroup(); + } + if (TryComplete()) + { + // All promises are complete. + // We just pass the state here and don't do anything about the exceptions, + // because the attached promise will handle the actual completion logic. + HandleNextInternal(_completeState); + } + } + + internal void MarkReady(uint totalPromises) + { + // This method is called after all promises have been hooked up to this. + if (MarkReadyAndGetIsComplete(unchecked((int) totalPromises))) + { + // All promises already completed. + _next = PromiseCompletionSentinel.s_instance; + SetCompletionState(CompleteAndGetState()); + } + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class MergePromiseGroup : PromiseSingleAwait + { + private static GetResultDelegate s_getResult; + + [MethodImpl(InlineOption)] + private static MergePromiseGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new MergePromiseGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static MergePromiseGroup GetOrCreate(in TResult value, GetResultDelegate getResultFunc, bool isExtended) + { + s_getResult = getResultFunc; + var promise = GetOrCreate(); + promise.Reset(); + promise._result = value; + promise._isExtended = isExtended; + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + handler.SetCompletionState(state); + + var group = handler.UnsafeAs(); + var passthroughs = group._completedPassThroughs.TakeAndClear(); + while (passthroughs.IsNotEmpty) + { + var passthrough = passthroughs.Pop(); + var owner = passthrough.Owner; + var index = passthrough.Index; + s_getResult.Invoke(owner, index, ref _result); + if (owner.State == Promise.State.Rejected) + { + var exception = owner._rejectContainer.GetValueAsException(); + if (_isExtended & index == 0) + { + // If this is an extended merge group, we need to extract the inner exceptions of the first cluster to make a flattened AggregateException. + // We only do this for 1 layer instead of using the Flatten method of the AggregateException, because we only want to flatten the exceptions from the group, + // and not any nested AggregateExceptions from the actual async work. + foreach (var ex in exception.UnsafeAs().InnerExceptions) + { + group.RecordException(ex); + } + } + else + { + group.RecordException(exception); + } + } + passthrough.Dispose(); + } + + if (group._exceptions != null) + { + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(group._exceptions), int.MinValue, null, this); + group._exceptions = null; + } + else + { + // The group may have been completed by a void promise, in which case it already converted its exceptions to a reject container. + _rejectContainer = handler._rejectContainer; + } + group.MaybeDispose(); + + HandleNextInternal(state); + } + } + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.MergePromiseGroupVoid GetOrCreateMergePromiseGroupVoid(CancelationRef cancelationSource) + => PromiseRefBase.MergePromiseGroupVoid.GetOrCreate(cancelationSource); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.MergePromiseGroup GetOrCreateMergePromiseGroup(in TResult value, GetResultDelegate getResultFunc, bool isExtended) + => PromiseRefBase.MergePromiseGroup.GetOrCreate(value, getResultFunc, isExtended); + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ThrowInvalidMergeGroup(int skipFrames) + => throw new InvalidOperationException("The promise merge group is invalid.", GetFormattedStacktrace(skipFrames + 1)); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs.meta new file mode 100644 index 00000000..60e95ff4 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseMergeGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 85229a41fec505a44a06f97aff180b2d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs new file mode 100644 index 00000000..43d820d8 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs @@ -0,0 +1,98 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class MergePromiseResultsGroup : PromiseSingleAwait + { + private static GetResultDelegate s_getResult; + + [MethodImpl(InlineOption)] + private static MergePromiseResultsGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new MergePromiseResultsGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static MergePromiseResultsGroup GetOrCreate(in TResult value, GetResultDelegate getResultFunc, bool isExtended) + { + s_getResult = getResultFunc; + var promise = GetOrCreate(); + promise.Reset(); + promise._result = value; + promise._isExtended = isExtended; + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + handler.SetCompletionState(state); + + // If any of the promises in the group completed unsuccessfully, the group state was set to canceled. + // We ignore that and set it to always resolved, because we're yielding a ValueTuple of ResultContainers. + state = Promise.State.Resolved; + var group = handler.UnsafeAs(); + var passthroughs = group._completedPassThroughs.TakeAndClear(); + while (passthroughs.IsNotEmpty) + { + var passthrough = passthroughs.Pop(); + var owner = passthrough.Owner; + var index = passthrough.Index; + s_getResult.Invoke(owner, index, ref _result); + if (owner.State == Promise.State.Rejected & _isExtended & index == 0) + { + // If this is an extended merge group, we need to propagate the exceptions from cancelation token callbacks. + state = Promise.State.Rejected; + _rejectContainer = owner._rejectContainer; + } + passthrough.Dispose(); + } + + if (group._exceptions != null) + { + // In case any cancelation token callbacks threw, we propagate them out of this promise instead of resolving this and ignoring the exceptions. + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(group._exceptions), int.MinValue, null, this); + group._exceptions = null; + } + else if (handler._rejectContainer != null) + { + // The group may have been already completed, in which case it already converted its exceptions to a reject container. + state = Promise.State.Rejected; + _rejectContainer = handler._rejectContainer; + } + group.MaybeDispose(); + + HandleNextInternal(state); + } + } + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.MergePromiseResultsGroup GetOrCreateMergePromiseResultsGroup(in TResult value, GetResultDelegate getResultFunc, bool isExtended) + => PromiseRefBase.MergePromiseResultsGroup.GetOrCreate(value, getResultFunc, isExtended); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs.meta new file mode 100644 index 00000000..0afd4be8 --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseMergeResultsGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0377e094784fd742b6a505de0e25a11 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs b/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs new file mode 100644 index 00000000..be2be13e --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs @@ -0,0 +1,303 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal abstract partial class RacePromiseGroupBase : PromiseGroupBase + { + protected void Complete() + { + HandleNextInternal(CompleteAndGetState()); + } + + private Promise.State CompleteAndGetState() + { + var state = _completeState; + // Race promise group ignores rejections if any promise was resolved, + // unless a cancelation token callback threw. + if ((state != Promise.State.Resolved | _cancelationThrew) & _exceptions != null) + { + state = Promise.State.Rejected; + _rejectContainer = CreateRejectContainer(new AggregateException(_exceptions), int.MinValue, null, this); + } + _exceptions = null; + return state; + } + + [MethodImpl(InlineOption)] + internal void SetResolved() + { + // We don't need to branch for VoidResult. + _isResolved = 1; + _completeState = Promise.State.Resolved; + CancelGroup(); + } + + [MethodImpl(InlineOption)] + internal void SetResolved(in TResult result) + { + if (Interlocked.Exchange(ref _isResolved, 1) == 0) + { + _completeState = Promise.State.Resolved; + _result = result; + } + CancelGroup(); + } + + new protected void CancelGroup() + { + // This may be called multiple times. It's fine because it checks internally if it's already canceled. + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + _cancelationThrew = true; + RecordException(e); + } + } + + [MethodImpl(InlineOption)] + internal void MarkReady(uint totalPromises) + => MarkReady(unchecked((int) totalPromises)); + + internal void MarkReady(int totalPromises) + { + // This method is called after all promises have been hooked up to this. + if (MarkReadyAndGetIsComplete(totalPromises)) + { + // All promises already completed. + _next = PromiseCompletionSentinel.s_instance; + SetCompletionState(CompleteAndGetState()); + } + } + + [MethodImpl(InlineOption)] + protected void Reset(CancelationRef cancelationSource, bool cancelOnNonResolved) + { + Reset(cancelationSource); + _completeState = Promise.State.Canceled; // Default to Canceled state. If the promise is actually resolved or rejected, the state will be overwritten. + _isResolved = 0; + _cancelOnNonResolved = cancelOnNonResolved; + _cancelationThrew = false; + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class RacePromiseGroup : RacePromiseGroupBase + { + [MethodImpl(InlineOption)] + private static RacePromiseGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new RacePromiseGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static RacePromiseGroup GetOrCreate(CancelationRef cancelationSource, bool cancelOnNonResolved) + { + var promise = GetOrCreate(); + promise.Reset(cancelationSource, cancelOnNonResolved); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + RemovePromiseAndSetCompletionState(handler, state); + if (state == Promise.State.Resolved) + { + if (Interlocked.Exchange(ref _isResolved, 1) == 0) + { + _completeState = Promise.State.Resolved; + _result = handler.GetResult(); + } + CancelGroup(); + } + else + { + if (state == Promise.State.Rejected) + { + RecordException(handler._rejectContainer.GetValueAsException()); + } + if (_cancelOnNonResolved) + { + CancelGroup(); + } + } + handler.MaybeDispose(); + if (TryComplete()) + { + // All promises are complete. + Complete(); + } + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class RacePromiseWithIndexGroupVoid : RacePromiseGroupBase + { + [MethodImpl(InlineOption)] + private static RacePromiseWithIndexGroupVoid GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid(); + return obj == InvalidAwaitSentinel.s_instance + ? new RacePromiseWithIndexGroupVoid() + : obj.UnsafeAs(); + } + + [MethodImpl(InlineOption)] + internal static RacePromiseWithIndexGroupVoid GetOrCreate(CancelationRef cancelationSource, bool cancelOnNonResolved) + { + var promise = GetOrCreate(); + promise.Reset(cancelationSource, cancelOnNonResolved); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state, int index) + { + RemovePromiseAndSetCompletionState(handler, state); + if (state == Promise.State.Resolved) + { + if (Interlocked.Exchange(ref _isResolved, 1) == 0) + { + _completeState = Promise.State.Resolved; + _result = index; + } + CancelGroup(); + } + else + { + if (state == Promise.State.Rejected) + { + RecordException(handler._rejectContainer.GetValueAsException()); + } + if (_cancelOnNonResolved) + { + CancelGroup(); + } + } + handler.MaybeDispose(); + if (TryComplete()) + { + // All promises are complete. + Complete(); + } + } + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class RacePromiseWithIndexGroup : RacePromiseGroupBase<(int, TResult)> + { + [MethodImpl(InlineOption)] + private static RacePromiseWithIndexGroup GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new RacePromiseWithIndexGroup() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static RacePromiseWithIndexGroup GetOrCreate(CancelationRef cancelationSource, bool cancelOnNonResolved) + { + var promise = GetOrCreate(); + promise.Reset(cancelationSource, cancelOnNonResolved); + return promise; + } + + internal override void MaybeDispose() + { + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state, int index) + { + RemovePromiseAndSetCompletionState(handler, state); + if (state == Promise.State.Resolved) + { + if (Interlocked.Exchange(ref _isResolved, 1) == 0) + { + _completeState = Promise.State.Resolved; + _result = (index, handler.GetResult()); + } + CancelGroup(); + } + else + { + if (state == Promise.State.Rejected) + { + RecordException(handler._rejectContainer.GetValueAsException()); + } + if (_cancelOnNonResolved) + { + CancelGroup(); + } + } + handler.MaybeDispose(); + if (TryComplete()) + { + // All promises are complete. + Complete(); + } + } + } + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.RacePromiseGroup GetOrCreateRacePromiseGroup(CancelationRef cancelationSource, bool cancelOnNonResolved) + => PromiseRefBase.RacePromiseGroup.GetOrCreate(cancelationSource, cancelOnNonResolved); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.RacePromiseWithIndexGroupVoid GetOrCreateRacePromiseWithIndexGroupVoid(CancelationRef cancelationSource, bool cancelOnNonResolved) + => PromiseRefBase.RacePromiseWithIndexGroupVoid.GetOrCreate(cancelationSource, cancelOnNonResolved); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.RacePromiseWithIndexGroup GetOrCreateRacePromiseWithIndexGroup(CancelationRef cancelationSource, bool cancelOnNonResolved) + => PromiseRefBase.RacePromiseWithIndexGroup.GetOrCreate(cancelationSource, cancelOnNonResolved); + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ThrowInvalidRaceGroup(int skipFrames) + => throw new InvalidOperationException("The promise race group is invalid.", GetFormattedStacktrace(skipFrames + 1)); + + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void ThrowAtLeastOneRaceGroup(int skipFrames) + => throw new InvalidOperationException("The promise race group must have at least one promise added to it.", GetFormattedStacktrace(skipFrames + 1)); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs.meta b/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs.meta new file mode 100644 index 00000000..3c99fa8b --- /dev/null +++ b/Package/Core/PromiseGroups/Internal/PromiseRaceGroupInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 579d02a2ae65ff74e89d8f0b18dac144 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseAllGroup.cs b/Package/Core/PromiseGroups/PromiseAllGroup.cs new file mode 100644 index 00000000..922fa04e --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseAllGroup.cs @@ -0,0 +1,159 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + /// + /// A structured concurrency group used to merge promises of a type. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseAllGroup + { + private readonly IList _valueContainer; + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.AllPromiseGroup _group; + private readonly int _cancelationId; + private readonly int _count; + private readonly int _index; + private readonly short _groupId; + + [MethodImpl(Internal.InlineOption)] + private PromiseAllGroup(IList valueContainer, Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.AllPromiseGroup group, int count, int index, short groupId) + { + _valueContainer = valueContainer; + _cancelationRef = cancelationRef; + _cancelationId = cancelationRef.SourceId; + _group = group; + _count = count; + _index = index; + _groupId = groupId; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllGroup New(out CancelationToken groupCancelationToken, IList valueContainer = null) + => New(CancelationToken.None, out groupCancelationToken, valueContainer); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled if is canceled or any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, IList valueContainer = null) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseAllGroup(valueContainer ?? new List(), cancelationRef, null, 0, 0, 0); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseAllGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(promise._result, index); + // We don't need to do anything else if the ref is null. + if (promise._ref != null) + { + ++count; + group.AddPromiseForMerge(promise._ref, promise._id, index); + } + return new PromiseAllGroup(list, cancelationRef, group, count, index + 1, group.Id); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(promise._result, index); + if (promise._ref != null) + { + group = Internal.GetOrCreateAllPromiseGroup(cancelationRef, list); + group.AddPromiseForMerge(promise._ref, promise._id, index); + return new PromiseAllGroup(list, cancelationRef, group, 1, index + 1, group.Id); + } + + return new PromiseAllGroup(list, cancelationRef, null, 0, index + 1, 0); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a list containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise> WaitAsync() + { + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + return Promise.Resolved(list); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + group.MarkReady(count); + return new Promise>(group, group.Id); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseAllGroup.cs.meta b/Package/Core/PromiseGroups/PromiseAllGroup.cs.meta new file mode 100644 index 00000000..b9df78ff --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseAllGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 491abe4f02e62774183a32ef4cf9e66a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs b/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs new file mode 100644 index 00000000..386281ff --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs @@ -0,0 +1,300 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + /// + /// A structured concurrency group used to merge promises and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseAllResultsGroup + { + private readonly IList _valueContainer; + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.AllPromiseResultsGroupVoid _group; + private readonly int _cancelationId; + private readonly int _count; + private readonly int _index; + private readonly short _groupId; + + [MethodImpl(Internal.InlineOption)] + private PromiseAllResultsGroup(IList valueContainer, Internal.CancelationRef cancelationRef, + Internal.PromiseRefBase.AllPromiseResultsGroupVoid group, int count, int index, short groupId) + { + _valueContainer = valueContainer; + _cancelationRef = cancelationRef; + _cancelationId = cancelationRef.SourceId; + _group = group; + _count = count; + _index = index; + _groupId = groupId; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllResultsGroup New(out CancelationToken groupCancelationToken, IList valueContainer = null) + => New(CancelationToken.None, out groupCancelationToken, valueContainer); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled if is canceled or any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllResultsGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, IList valueContainer = null) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseAllResultsGroup(valueContainer ?? new List(), cancelationRef, null, 0, 0, 0); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseAllResultsGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(Promise.ResultContainer.Resolved, index); + // We don't need to do anything else if the ref is null. + if (promise._ref != null) + { + ++count; + group.AddPromiseForMerge(promise._ref, promise._id, index); + } + return new PromiseAllResultsGroup(list, cancelationRef, group, count, index + 1, group.Id); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(Promise.ResultContainer.Resolved, index); + if (promise._ref != null) + { + group = Internal.GetOrCreateAllPromiseResultsGroup(cancelationRef, list); + group.AddPromiseForMerge(promise._ref, promise._id, index); + return new PromiseAllResultsGroup(list, cancelationRef, group, 1, index + 1, group.Id); + } + + return new PromiseAllResultsGroup(list, cancelationRef, null, 0, index + 1, 0); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a list containing their results. + /// + public Promise> WaitAsync() + { + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + return Promise.Resolved(list); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + group.MarkReady(count); + return new Promise>(group, group.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of a type and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseAllResultsGroup + { + private readonly IList.ResultContainer> _valueContainer; + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.AllPromiseResultsGroup _group; + private readonly int _cancelationId; + private readonly int _count; + private readonly int _index; + private readonly short _id; + + [MethodImpl(Internal.InlineOption)] + private PromiseAllResultsGroup(IList.ResultContainer> valueContainer, Internal.CancelationRef cancelationRef, + Internal.PromiseRefBase.AllPromiseResultsGroup group, int count, int index, short groupId) + { + _valueContainer = valueContainer; + _cancelationRef = cancelationRef; + _cancelationId = cancelationRef.SourceId; + _group = group; + _count = count; + _index = index; + _id = groupId; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllResultsGroup New(out CancelationToken groupCancelationToken, IList.ResultContainer> valueContainer = null) + => New(CancelationToken.None, out groupCancelationToken, valueContainer); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled if is canceled or any of the promises in the group are rejected or canceled. + /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. + public static PromiseAllResultsGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, IList.ResultContainer> valueContainer = null) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseAllResultsGroup(valueContainer ?? new List.ResultContainer>(), cancelationRef, null, 0, 0, 0); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseAllResultsGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_id)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(promise._result, index); + // We don't need to do anything else if the ref is null. + if (promise._ref != null) + { + ++count; + group.AddPromiseForMerge(promise._ref, promise._id, index); + } + return new PromiseAllResultsGroup(list, cancelationRef, group, count, index + 1, group.Id); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // We don't protect the list with a lock, because we ensure this is only used by a single caller. + list.SetOrAdd(promise._result, index); + if (promise._ref != null) + { + group = Internal.GetOrCreateAllPromiseResultsGroup(cancelationRef, list); + group.AddPromiseForMerge(promise._ref, promise._id, index); + return new PromiseAllResultsGroup(list, cancelationRef, group, 1, index + 1, group.Id); + } + + return new PromiseAllResultsGroup(list, cancelationRef, null, 0, index + 1, 0); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a list containing their results. + /// + public Promise.ResultContainer>> WaitAsync() + { + var list = _valueContainer; + var cancelationRef = _cancelationRef; + var group = _group; + int count = _count; + int index = _index; + if (cancelationRef == null | list == null) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + return Promise.Resolved(list); + } + + if (!group.TryIncrementId(_id)) + { + Internal.ThrowInvalidAllGroup(1); + } + + // Make sure list has the same count as promises. + list.MaybeShrink(index); + group.MarkReady(count); + return new Promise.ResultContainer>>(group, group.Id); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs.meta b/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs.meta new file mode 100644 index 00000000..d1c5d222 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseAllResultsGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: e6254f4aa9182614aaa6539223eebf99 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseEachGroup.cs b/Package/Core/PromiseGroups/PromiseEachGroup.cs new file mode 100644 index 00000000..9654740a --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseEachGroup.cs @@ -0,0 +1,275 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using Proto.Promises.Linq; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Proto.Promises +{ + /// + /// A structured concurrency group used to yield promise results as they complete. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseEachGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.EachPromiseGroup _group; + private readonly int _cancelationId; + private readonly int _groupId; + private readonly int _pendingCount; + private readonly int _totalCount; + + [MethodImpl(Internal.InlineOption)] + private PromiseEachGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.EachPromiseGroup group, + int groupId, int pendingCount, int totalCount) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _groupId = groupId; + _pendingCount = pendingCount; + _totalCount = totalCount; + } + + /// + /// Get a new and the tied to it. + /// + /// + /// The token that will be canceled when the associated is disposed, + /// or when the associated 's is canceled, if it has one attached. + /// + public static PromiseEachGroup New(out CancelationToken groupCancelationToken) + => New(CancelationToken.None, out groupCancelationToken); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// + /// The token that will be canceled when is canceled, + /// or when the associated is disposed, + /// or when the associated 's is canceled, if it has one attached. + /// + /// + /// If the is canceled before the iteration is complete, iteration will not be canceled. + /// To cancel the iteration, use on the returned from . + /// + public static PromiseEachGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseEachGroup(cancelationRef, null, 0, 0, 0); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseEachGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + var pendingCount = _pendingCount; + var totalCount = _totalCount; + if (group == null) + { + if (cancelationRef?.TryIncrementSourceId(_cancelationId) != true) + { + Internal.ThrowInvalidEachGroup(1); + } + + group = Internal.GetOrCreateEachPromiseGroup(cancelationRef); + } + else if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidEachGroup(1); + } + + checked { ++totalCount; } + if (promise._ref == null) + { + group.AddResult(Promise.ResultContainer.Resolved); + } + else + { + ++pendingCount; + group.AddPromise(promise._ref, promise._id); + } + return new PromiseEachGroup(cancelationRef, group, group.EnumerableId, pendingCount, totalCount); + } + + /// + /// Gets the that will yield the result of each promise as they complete. + /// + /// + /// If , unobserved rejections will be suppressed. + /// Otherwise, if the associated is disposed before all results were observed, and any promise was rejected, + /// the promise will be rejected with an containing all of the unobserved rejections. + /// + public AsyncEnumerable GetAsyncEnumerable(bool suppressUnobservedRejections = false) + { + var cancelationRef = _cancelationRef; + var group = _group; + var pendingCount = _pendingCount; + var totalCount = _totalCount; + if (group == null) + { + if (cancelationRef?.TryDispose(_cancelationId) != true) + { + Internal.ThrowInvalidEachGroup(1); + } + return AsyncEnumerable.Empty(); + } + else if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidEachGroup(1); + } + + group.MarkReady(suppressUnobservedRejections, pendingCount, totalCount); + return new AsyncEnumerable(group); + } + } + + /// + /// A structured concurrency group used to yield promise results as they complete. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + [StructLayout(LayoutKind.Auto)] + public readonly struct PromiseEachGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.EachPromiseGroup.ResultContainer> _group; + private readonly int _cancelationId; + private readonly int _groupId; + private readonly int _pendingCount; + private readonly int _totalCount; + + [MethodImpl(Internal.InlineOption)] + private PromiseEachGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.EachPromiseGroup.ResultContainer> group, + int groupId, int pendingCount, int totalCount) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _groupId = groupId; + _pendingCount = pendingCount; + _totalCount = totalCount; + } + + /// + /// Get a new and the tied to it. + /// + /// + /// The token that will be canceled when the associated is disposed, + /// or when the associated 's is canceled, if it has one attached. + /// + public static PromiseEachGroup New(out CancelationToken groupCancelationToken) + => New(CancelationToken.None, out groupCancelationToken); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// + /// The token that will be canceled when is canceled, + /// or when the associated is disposed, + /// or when the associated 's is canceled, if it has one attached. + /// + /// + /// If the is canceled before the iteration is complete, iteration will not be canceled. + /// To cancel the iteration, use on the returned from . + /// + public static PromiseEachGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseEachGroup(cancelationRef, null, 0, 0, 0); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseEachGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + var pendingCount = _pendingCount; + var totalCount = _totalCount; + if (group == null) + { + if (cancelationRef?.TryIncrementSourceId(_cancelationId) != true) + { + Internal.ThrowInvalidEachGroup(1); + } + + group = Internal.GetOrCreateEachPromiseGroup(cancelationRef); + } + else if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidEachGroup(1); + } + + checked { ++totalCount; } + if (promise._ref == null) + { + group.AddResult(promise._result); + } + else + { + ++pendingCount; + group.AddPromise(promise._ref, promise._id); + } + return new PromiseEachGroup(cancelationRef, group, group.EnumerableId, pendingCount, totalCount); + } + + /// + /// Gets the that will yield the result of each promise as they complete. + /// + /// + /// If , unobserved rejections will be suppressed. + /// Otherwise, if the associated is disposed before all results were observed, and any promise was rejected, + /// the promise will be rejected with an containing all of the unobserved rejections. + /// + public AsyncEnumerable.ResultContainer> GetAsyncEnumerable(bool suppressUnobservedRejections = false) + { + var cancelationRef = _cancelationRef; + var group = _group; + var pendingCount = _pendingCount; + var totalCount = _totalCount; + if (group == null) + { + if (cancelationRef?.TryDispose(_cancelationId) != true) + { + Internal.ThrowInvalidEachGroup(1); + } + return AsyncEnumerable.ResultContainer>.Empty(); + } + else if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidEachGroup(1); + } + + group.MarkReady(suppressUnobservedRejections, pendingCount, totalCount); + return new AsyncEnumerable.ResultContainer>(group); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseEachGroup.cs.meta b/Package/Core/PromiseGroups/PromiseEachGroup.cs.meta new file mode 100644 index 00000000..f4f12e9f --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseEachGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 1d5a3cb0390977945a1db54d83c4e93d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseMergeGroup.cs b/Package/Core/PromiseGroups/PromiseMergeGroup.cs new file mode 100644 index 00000000..a627cb97 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseMergeGroup.cs @@ -0,0 +1,699 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +#pragma warning disable IDE0090 // Use 'new(...)' + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + // Promise merge groups use 1 backing reference if only void promises are merged, + // 2 backing references if a promise of any other type is merged. + // The first one is to merge the promises before the final type is known, + // the second one is to realize the actual type from WaitAsync. + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + internal readonly Internal.CancelationRef _cancelationRef; + internal readonly Internal.PromiseRefBase.MergePromiseGroupVoid _group; + internal readonly int _cancelationId; + internal readonly uint _count; + internal readonly short _groupId; + internal readonly bool _isExtended; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(Internal.CancelationRef cancelationRef, bool isExtended = false) : this(cancelationRef, null, 0, 0, isExtended) + { + } + + [MethodImpl(Internal.InlineOption)] + private PromiseMergeGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.MergePromiseGroupVoid group, uint count, short groupId, bool isExtended) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _count = count; + _groupId = groupId; + _isExtended = isExtended; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + public static PromiseMergeGroup New(out CancelationToken groupCancelationToken) + => New(CancelationToken.None, out groupCancelationToken); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled if is canceled or any of the promises in the group are rejected or canceled. + public static PromiseMergeGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseMergeGroup(cancelationRef); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + uint count = _count; + var isExtended = _isExtended; + if (cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + + // We don't need to do anything if the ref is null. + if (promise._ref != null) + { + checked { ++count; } + group.AddPromise(promise._ref, promise._id); + } + return new PromiseMergeGroup(cancelationRef, group, count, group.Id, isExtended); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateMergePromiseGroupVoid(cancelationRef); + group.AddPromise(promise._ref, promise._id); + return new PromiseMergeGroup(cancelationRef, group, 1, group.Id, isExtended); + } + + return new PromiseMergeGroup(cancelationRef, isExtended); + } + + internal PromiseMergeGroup Merge(Promise promise, int index) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 2); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + uint count = _count; + var isExtended = _isExtended; + if (cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(2); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + // We don't need to do anything if the ref is null. + if (promise._ref != null) + { + checked { ++count; } + group.AddPromiseForMerge(promise._ref, promise._id, index); + } + return new PromiseMergeGroup(cancelationRef, group, count, group.Id, isExtended); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateMergePromiseGroupVoid(cancelationRef); + group.AddPromiseForMerge(promise._ref, promise._id, index); + return new PromiseMergeGroup(cancelationRef, group, 1, group.Id, isExtended); + } + + return new PromiseMergeGroup(cancelationRef, isExtended); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(Merge(promise, 0), promise._result); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise WaitAsync() + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + if (cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + return Promise.Resolved(); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(count); + return new Promise(group, group.Id); + } + + [MethodImpl(Internal.InlineOption)] + internal void DisposeCancelationOrThrow() + { + if (!_cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + } + + internal PromiseMergeGroup MergeForExtension(Internal.PromiseRefBase promise, short id) + { + // We don't do any validation checks here, because they were already done in the caller. + var group = Internal.GetOrCreateMergePromiseGroupVoid(_cancelationRef); + group.AddPromiseForMerge(promise, id, 0); + return new PromiseMergeGroup(_cancelationRef, group, 1, group.Id, true); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly T1 _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in T1 value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 1), (_value, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with the resolved value. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetOne(), false); + group.HookupNewPromise(group.Id, promise); + return new Promise(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 2), (_value.Item1, _value.Item2, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetTwo(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2, T3) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 3), (_value.Item1, _value.Item2, _value.Item3, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2, T3)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetThree(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 4), (_value.Item1, _value.Item2, _value.Item3, _value.Item4, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2, T3, T4)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetFour(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 5), (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2, T3, T4, T5)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetFive(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Merge(promise, 6), (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, _value.Item6, promise._result)); + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2, T3, T4, T5, T6)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetSix(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeGroup + { + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6, T7) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeGroup(PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6, T7) value) + { + _mergeGroup = mergeGroup; + _value = value; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeGroup Add(Promise promise) + => new PromiseMergeGroup(_mergeGroup.Add(promise), _value); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + // Merging more than 7 types should be fairly rare. To support N types greater than 7, we just wrap it in another group. + public PromiseMergeGroup<(T1, T2, T3, T4, T5, T6, T7), T8> Add(Promise promise) + => new PromiseMergeGroup<(T1, T2, T3, T4, T5, T6, T7)>(SetupExtension(), _value).Add(promise); + + private PromiseMergeGroup SetupExtension() + { + var mergeGroup = _mergeGroup; + // We're wrapping this in another group, so we just increment its SourceId instead of disposing. + if (mergeGroup._cancelationRef == null || !mergeGroup._cancelationRef.TryIncrementSourceId(mergeGroup._cancelationId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + var group = mergeGroup._group; + if (group == null) + { + return new PromiseMergeGroup(mergeGroup._cancelationRef, true); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetSeven(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + + return new PromiseMergeGroup(mergeGroup._cancelationRef, true) + .MergeForExtension(promise, promise.Id); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If all promises are resolved, the returned promise will be resolved with a tuple containing each of their resolved values. + /// If any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if any promise is canceled, the returned promise will be canceled. + /// + public Promise<(T1, T2, T3, T4, T5, T6, T7)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseGroup(_value, Promise.MergeResultFuncs.GetSeven(), mergeGroup._isExtended); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6, T7)>(promise, promise.Id); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseMergeGroup.cs.meta b/Package/Core/PromiseGroups/PromiseMergeGroup.cs.meta new file mode 100644 index 00000000..35894b49 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseMergeGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 03689cd8e5d0e4f4eb774101b97f2b80 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs b/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs new file mode 100644 index 00000000..b048e537 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs @@ -0,0 +1,1787 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +#pragma warning disable IDE0090 // Use 'new(...)' + +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + // Promise merge results groups use 2 backing references. + // The first one is to merge the promises before the final type is known, + // the second one is to realize the actual type from WaitAsync. + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + private readonly PromiseMergeGroup _mergeGroup; + + [MethodImpl(Internal.InlineOption)] + private PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup) + { + _mergeGroup = mergeGroup; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + public static PromiseMergeResultsGroup New(out CancelationToken groupCancelationToken) + => New(CancelationToken.None, out groupCancelationToken); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled if is canceled or any of the promises in the group are rejected or canceled. + public static PromiseMergeResultsGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken) + => new PromiseMergeResultsGroup(PromiseMergeGroup.New(sourceCancelationToken, out groupCancelationToken)); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 0), + Promise.ResultContainer.Resolved, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 0), + promise._result, + Promise.MergeResultFuncs.GetMergeResult()); + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic type can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegate. + private static Internal.GetResultDelegate s_getResult1Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly T1 _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in T1 value, Internal.GetResultDelegate getResult1Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 1), + (_value, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 1), + (_value, promise._result), + s_getResult1Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 2), + (_value.Item1, _value.Item2, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + s_getResult2Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 2), + (_value.Item1, _value.Item2, promise._result), + s_getResult1Delegate, + s_getResult2Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2, T3) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 3), + (_value.Item1, _value.Item2, _value.Item3, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 3), + (_value.Item1, _value.Item2, _value.Item3, promise._result), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 4), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 4), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, promise._result), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 5), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 5), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, promise._result), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + private static Internal.GetResultDelegate s_getResult6Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + s_getResult6Delegate = getResult6Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup Add(Promise promise) + => new PromiseMergeResultsGroup( + _mergeGroup.Merge(promise, 6), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, _value.Item6, Promise.ResultContainer.Resolved), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroup.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroup.ResultContainer>( + _mergeGroup.Merge(promise, 6), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, _value.Item6, promise._result), + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5, T6)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroup + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult1Delegate; + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + private static Internal.GetResultDelegate s_getResult6Delegate; + private static Internal.GetResultDelegate s_getResult7Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6, T7) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroup(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6, T7) value, + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate, + Internal.GetResultDelegate getResult7Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult1Delegate = getResult1Delegate; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + s_getResult6Delegate = getResult6Delegate; + s_getResult7Delegate = getResult7Delegate; + } + + // Merging more than 7 types should be fairly rare. To support N types greater than 7, we use PromiseMergeResultsGroupExtended. + // We use PromiseMergeResultsGroupExtended instead of wrapping it in another PromiseMergeResultsGroup to avoid any issues with users combining ValueTuples of ResultContainers, + // so that the GetResultDelegates will always be correct (they are stored statically per type). + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> + ( + SetupExtension().Merge(promise, 1), + (_value, Promise.ResultContainer.Resolved), + Promise.MergeResultFuncs.GetMergeResultVoid() + ); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> + ( + SetupExtension().Merge(promise, 1), + (_value, promise._result), + Promise.MergeResultFuncs.GetMergeResult() + ); + + private PromiseMergeGroup SetupExtension() + { + var mergeGroup = _mergeGroup; + // We're wrapping this in another group, so we just increment its SourceId instead of disposing. + if (mergeGroup._cancelationRef == null || !mergeGroup._cancelationRef.TryIncrementSourceId(mergeGroup._cancelationId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + var group = mergeGroup._group; + if (group == null) + { + return new PromiseMergeGroup(mergeGroup._cancelationRef, true); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + s_getResult7Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + + return new PromiseMergeGroup(mergeGroup._cancelationRef, true) + .MergeForExtension(promise, promise.Id); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5, T6, T7)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + s_getResult1Delegate, + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + s_getResult7Delegate), + false + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6, T7)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2) value, + Internal.GetResultDelegate getResult2Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended Add(Promise promise) + => new PromiseMergeResultsGroupExtended( + _mergeGroup.Merge(promise, 2), + (_value.Item1, _value.Item2, Promise.ResultContainer.Resolved), + s_getResult2Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended.ResultContainer>( + _mergeGroup.Merge(promise, 2), + (_value.Item1, _value.Item2, promise._result), + s_getResult2Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2, T3) value, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended Add(Promise promise) + => new PromiseMergeResultsGroupExtended( + _mergeGroup.Merge(promise, 3), + (_value.Item1, _value.Item2, _value.Item3, Promise.ResultContainer.Resolved), + s_getResult2Delegate, + s_getResult3Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended.ResultContainer>( + _mergeGroup.Merge(promise, 3), + (_value.Item1, _value.Item2, _value.Item3, promise._result), + s_getResult2Delegate, + s_getResult3Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4) value, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended Add(Promise promise) + => new PromiseMergeResultsGroupExtended( + _mergeGroup.Merge(promise, 4), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, Promise.ResultContainer.Resolved), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended.ResultContainer>( + _mergeGroup.Merge(promise, 4), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, promise._result), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5) value, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended Add(Promise promise) + => new PromiseMergeResultsGroupExtended( + _mergeGroup.Merge(promise, 5), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, Promise.ResultContainer.Resolved), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended.ResultContainer>( + _mergeGroup.Merge(promise, 5), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, promise._result), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + private static Internal.GetResultDelegate s_getResult6Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6) value, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + s_getResult6Delegate = getResult6Delegate; + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended Add(Promise promise) + => new PromiseMergeResultsGroupExtended( + _mergeGroup.Merge(promise, 6), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, _value.Item6, Promise.ResultContainer.Resolved), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + Promise.MergeResultFuncs.GetMergeResultVoid()); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended.ResultContainer>( + _mergeGroup.Merge(promise, 6), + (_value.Item1, _value.Item2, _value.Item3, _value.Item4, _value.Item5, _value.Item6, promise._result), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + Promise.MergeResultFuncs.GetMergeResult()); + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5, T6)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6)>(promise, promise.Id); + } + } + + /// + /// A structured concurrency group used to merge promises of one or more types and yield their results. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseMergeResultsGroupExtended + { + // The generic types can be Promise.ResultContainer, or Promise.ResultContainer. + // In order to resolve the correct type, we have to store the GetResultDelegates. + private static Internal.GetResultDelegate s_getResult2Delegate; + private static Internal.GetResultDelegate s_getResult3Delegate; + private static Internal.GetResultDelegate s_getResult4Delegate; + private static Internal.GetResultDelegate s_getResult5Delegate; + private static Internal.GetResultDelegate s_getResult6Delegate; + private static Internal.GetResultDelegate s_getResult7Delegate; + + private readonly PromiseMergeGroup _mergeGroup; + private readonly (T1, T2, T3, T4, T5, T6, T7) _value; + + [MethodImpl(Internal.InlineOption)] + internal PromiseMergeResultsGroupExtended(in PromiseMergeGroup mergeGroup, in (T1, T2, T3, T4, T5, T6, T7) value, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate, + Internal.GetResultDelegate getResult7Delegate) + { + _mergeGroup = mergeGroup; + _value = value; + s_getResult2Delegate = getResult2Delegate; + s_getResult3Delegate = getResult3Delegate; + s_getResult4Delegate = getResult4Delegate; + s_getResult5Delegate = getResult5Delegate; + s_getResult6Delegate = getResult6Delegate; + s_getResult7Delegate = getResult7Delegate; + } + + // Merging more than 13 types should be rare. To support N types greater than 13, we wrap it in another PromiseMergeResultsGroupExtended. + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> + ( + SetupExtension().Merge(promise, 1), + (_value, Promise.ResultContainer.Resolved), + Promise.MergeResultFuncs.GetMergeResultVoid() + ); + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> Add(Promise promise) + => new PromiseMergeResultsGroupExtended<(T1, T2, T3, T4, T5, T6, T7), Promise.ResultContainer> + ( + SetupExtension().Merge(promise, 1), + (_value, promise._result), + Promise.MergeResultFuncs.GetMergeResult() + ); + + private PromiseMergeGroup SetupExtension() + { + var mergeGroup = _mergeGroup; + // We're wrapping this in another group, so we just increment its SourceId instead of disposing. + if (mergeGroup._cancelationRef == null || !mergeGroup._cancelationRef.TryIncrementSourceId(mergeGroup._cancelationId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + var group = mergeGroup._group; + if (group == null) + { + return new PromiseMergeGroup(mergeGroup._cancelationRef, true); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(2); + } + + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + s_getResult7Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + + return new PromiseMergeGroup(mergeGroup._cancelationRef, true) + .MergeForExtension(promise, promise.Id); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete and yields a tuple containing their results. + /// + public Promise<(T1, T2, T3, T4, T5, T6, T7)> WaitAsync() + { + var mergeGroup = _mergeGroup; + if (mergeGroup._cancelationRef == null) + { + Internal.ThrowInvalidMergeGroup(1); + } + + var group = mergeGroup._group; + if (group == null) + { + mergeGroup.DisposeCancelationOrThrow(); + return Promise.Resolved(_value); + } + + if (!group.TryIncrementId(mergeGroup._groupId)) + { + Internal.ThrowInvalidMergeGroup(1); + } + group.MarkReady(mergeGroup._count); + var promise = Internal.GetOrCreateMergePromiseResultsGroup(_value, + Promise.MergeResultFuncs.GetCombinedMerger( + Promise.MergeResultFuncs.GetMergeValue(), + s_getResult2Delegate, + s_getResult3Delegate, + s_getResult4Delegate, + s_getResult5Delegate, + s_getResult6Delegate, + s_getResult7Delegate), + true + ); + group.HookupNewPromise(group.Id, promise); + return new Promise<(T1, T2, T3, T4, T5, T6, T7)>(promise, promise.Id); + } + } + + partial struct Promise + { + static partial class MergeResultFuncs + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class MergeResult + { + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref Promise.ResultContainer result) + { + result = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate.ResultContainer> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate.ResultContainer> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate.ResultContainer> GetMergeResult() => MergeResult.Func; + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class MergeResultVoid + { + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref ResultContainer result) + { + result = new ResultContainer(handler._rejectContainer, handler.State); + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate GetMergeResultVoid() => MergeResultVoid.Func; + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class MergeValue + { + [MethodImpl(Internal.InlineOption)] + private static void GetMergeValue(Internal.PromiseRefBase handler, int index, ref T result) + { + result = handler.GetResult(); + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeValue); + } +#else + internal static readonly Internal.GetResultDelegate Func + = GetMergeValue; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate GetMergeValue() => MergeValue.Func; + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + return CombinedMerger.Func; + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + internal static Internal.GetResultDelegate s_getResult3Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2, T3) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + case 2: + s_getResult3Delegate.Invoke(handler, index, ref result.Item3); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2, T3)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2, T3)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2, T3)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + CombinedMerger.s_getResult3Delegate = getResult3Delegate; + return CombinedMerger.Func; + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + internal static Internal.GetResultDelegate s_getResult3Delegate; + internal static Internal.GetResultDelegate s_getResult4Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2, T3, T4) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + case 2: + s_getResult3Delegate.Invoke(handler, index, ref result.Item3); + break; + case 3: + s_getResult4Delegate.Invoke(handler, index, ref result.Item4); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2, T3, T4)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2, T3, T4)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2, T3, T4)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + CombinedMerger.s_getResult3Delegate = getResult3Delegate; + CombinedMerger.s_getResult4Delegate = getResult4Delegate; + return CombinedMerger.Func; + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + internal static Internal.GetResultDelegate s_getResult3Delegate; + internal static Internal.GetResultDelegate s_getResult4Delegate; + internal static Internal.GetResultDelegate s_getResult5Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2, T3, T4, T5) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + case 2: + s_getResult3Delegate.Invoke(handler, index, ref result.Item3); + break; + case 3: + s_getResult4Delegate.Invoke(handler, index, ref result.Item4); + break; + case 4: + s_getResult5Delegate.Invoke(handler, index, ref result.Item5); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2, T3, T4, T5)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2, T3, T4, T5)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2, T3, T4, T5)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + CombinedMerger.s_getResult3Delegate = getResult3Delegate; + CombinedMerger.s_getResult4Delegate = getResult4Delegate; + CombinedMerger.s_getResult5Delegate = getResult5Delegate; + return CombinedMerger.Func; + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + internal static Internal.GetResultDelegate s_getResult3Delegate; + internal static Internal.GetResultDelegate s_getResult4Delegate; + internal static Internal.GetResultDelegate s_getResult5Delegate; + internal static Internal.GetResultDelegate s_getResult6Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2, T3, T4, T5, T6) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + case 2: + s_getResult3Delegate.Invoke(handler, index, ref result.Item3); + break; + case 3: + s_getResult4Delegate.Invoke(handler, index, ref result.Item4); + break; + case 4: + s_getResult5Delegate.Invoke(handler, index, ref result.Item5); + break; + case 5: + s_getResult6Delegate.Invoke(handler, index, ref result.Item6); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + CombinedMerger.s_getResult3Delegate = getResult3Delegate; + CombinedMerger.s_getResult4Delegate = getResult4Delegate; + CombinedMerger.s_getResult5Delegate = getResult5Delegate; + CombinedMerger.s_getResult6Delegate = getResult6Delegate; + return CombinedMerger.Func; + } + +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + private static class CombinedMerger + { + internal static Internal.GetResultDelegate s_getResult1Delegate; + internal static Internal.GetResultDelegate s_getResult2Delegate; + internal static Internal.GetResultDelegate s_getResult3Delegate; + internal static Internal.GetResultDelegate s_getResult4Delegate; + internal static Internal.GetResultDelegate s_getResult5Delegate; + internal static Internal.GetResultDelegate s_getResult6Delegate; + internal static Internal.GetResultDelegate s_getResult7Delegate; + + [MethodImpl(Internal.InlineOption)] + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (T1, T2, T3, T4, T5, T6, T7) result) + { + switch (index) + { + case 0: + s_getResult1Delegate.Invoke(handler, index, ref result.Item1); + break; + case 1: + s_getResult2Delegate.Invoke(handler, index, ref result.Item2); + break; + case 2: + s_getResult3Delegate.Invoke(handler, index, ref result.Item3); + break; + case 3: + s_getResult4Delegate.Invoke(handler, index, ref result.Item4); + break; + case 4: + s_getResult5Delegate.Invoke(handler, index, ref result.Item5); + break; + case 5: + s_getResult6Delegate.Invoke(handler, index, ref result.Item6); + break; + case 6: + s_getResult7Delegate.Invoke(handler, index, ref result.Item7); + break; + } + } + +#if NETCOREAPP || UNITY_2021_2_OR_NEWER + internal static unsafe Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6, T7)> Func + { + [MethodImpl(Internal.InlineOption)] + get => new(&GetMergeResult); + } +#else + internal static readonly Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6, T7)> Func + = GetMergeResult; +#endif + } + + [MethodImpl(Internal.InlineOption)] + internal static Internal.GetResultDelegate<(T1, T2, T3, T4, T5, T6, T7)> GetCombinedMerger( + Internal.GetResultDelegate getResult1Delegate, + Internal.GetResultDelegate getResult2Delegate, + Internal.GetResultDelegate getResult3Delegate, + Internal.GetResultDelegate getResult4Delegate, + Internal.GetResultDelegate getResult5Delegate, + Internal.GetResultDelegate getResult6Delegate, + Internal.GetResultDelegate getResult7Delegate) + { + CombinedMerger.s_getResult1Delegate = getResult1Delegate; + CombinedMerger.s_getResult2Delegate = getResult2Delegate; + CombinedMerger.s_getResult3Delegate = getResult3Delegate; + CombinedMerger.s_getResult4Delegate = getResult4Delegate; + CombinedMerger.s_getResult5Delegate = getResult5Delegate; + CombinedMerger.s_getResult6Delegate = getResult6Delegate; + CombinedMerger.s_getResult7Delegate = getResult7Delegate; + return CombinedMerger.Func; + } + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs.meta b/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs.meta new file mode 100644 index 00000000..529ebb30 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseMergeResultsGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 0829684db388a8b4b8a13abe9a2254c3 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseRaceGroup.cs b/Package/Core/PromiseGroups/PromiseRaceGroup.cs new file mode 100644 index 00000000..5a4e14f8 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseRaceGroup.cs @@ -0,0 +1,345 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Proto.Promises +{ + /// + /// A structured concurrency group used to race promises. Waits for the first promise to resolve. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseRaceGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.RacePromiseGroup _group; + private readonly int _cancelationId; + private readonly uint _count; + private readonly short _groupId; + private readonly bool _cancelOnNonResolved; + private readonly bool _isResolved; + + [MethodImpl(Internal.InlineOption)] + private PromiseRaceGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.RacePromiseGroup group, + uint count, short groupId, bool cancelOnNonResolved, bool isResolved) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _count = count; + _groupId = groupId; + _cancelOnNonResolved = cancelOnNonResolved; + _isResolved = isResolved; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled when any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceGroup New(out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + => New(CancelationToken.None, out groupCancelationToken, cancelOnNonResolved); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled when is canceled or any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseRaceGroup(cancelationRef, null, 0, 0, cancelOnNonResolved, false); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseRaceGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var cancelOnNonResolved = _cancelOnNonResolved; + var isResolved = _isResolved; + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + checked { ++count; } + group.AddPromise(promise._ref, promise._id); + } + else + { + isResolved = true; + group.SetResolved(); + } + return new PromiseRaceGroup(cancelationRef, group, count, group.Id, cancelOnNonResolved, isResolved); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateRacePromiseGroup(cancelationRef, cancelOnNonResolved); + group.AddPromise(promise._ref, promise._id); + if (isResolved) + { + group.SetResolved(); + } + return new PromiseRaceGroup(cancelationRef, group, 1, group.Id, cancelOnNonResolved, isResolved); + } + + // The promise is already resolved, we need to cancel the group token, + // and catch any exceptions to propagate them out of WaitAsync(). + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + // We already canceled the group token, no need to cancel it again if a promise is non-resolved. + group = Internal.GetOrCreateRacePromiseGroup(cancelationRef, false); + group.RecordException(e); + group._cancelationThrew = true; + return new PromiseRaceGroup(cancelationRef, group, 0, group.Id, false, isResolved); + } + + return new PromiseRaceGroup(cancelationRef, group, 0, _groupId, false, true); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If any promise is resolved, the returned promise will be resolved. + /// If no promises are resolved and any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if all promises are canceled, the returned promise will be canceled. + /// + public Promise WaitAsync() + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + if (cancelationRef == null | (group == null & !_isResolved)) + { + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + Internal.ThrowAtLeastOneRaceGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidAllGroup(1); + } + return Promise.Resolved(); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + group.MarkReady(count); + return new Promise(group, group.Id); + } + } + + /// + /// A structured concurrency group used to race promises. Waits for the first promise to resolve. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + [StructLayout(LayoutKind.Auto)] + public readonly struct PromiseRaceGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.RacePromiseGroup _group; + private readonly int _cancelationId; + private readonly uint _count; + private readonly short _groupId; + private readonly bool _cancelOnNonResolved; + private readonly bool _isResolved; + private readonly T _result; + + [MethodImpl(Internal.InlineOption)] + private PromiseRaceGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.RacePromiseGroup group, + in T result, uint count, short groupId, bool cancelOnNonResolved, bool isResolved) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _result = result; + _count = count; + _groupId = groupId; + _cancelOnNonResolved = cancelOnNonResolved; + _isResolved = isResolved; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled when any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceGroup New(out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + => New(CancelationToken.None, out groupCancelationToken, cancelOnNonResolved); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled when is canceled or any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseRaceGroup(cancelationRef, null, default, 0, 0, cancelOnNonResolved, false); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseRaceGroup Add(Promise promise) + { +#if PROMISE_DEBUG + Internal.ValidateArgument(promise, nameof(promise), 1); +#endif + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var cancelOnNonResolved = _cancelOnNonResolved; + var isResolved = _isResolved; + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + checked { ++count; } + group.AddPromise(promise._ref, promise._id); + } + else if (!isResolved) + { + + isResolved = true; + group.SetResolved(promise._result); + } + return new PromiseRaceGroup(cancelationRef, group, default, count, group.Id, cancelOnNonResolved, isResolved); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateRacePromiseGroup(cancelationRef, cancelOnNonResolved); + group.AddPromise(promise._ref, promise._id); + if (isResolved) + { + group.SetResolved(_result); + } + return new PromiseRaceGroup(cancelationRef, group, default, 1, group.Id, cancelOnNonResolved, isResolved); + } + + // The promise is already resolved, we need to cancel the group token, + // and catch any exceptions to propagate them out of WaitAsync(). + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + // We already canceled the group token, no need to cancel it again if a promise is non-resolved. + group = Internal.GetOrCreateRacePromiseGroup(cancelationRef, false); + group.RecordException(e); + group._cancelationThrew = true; + return new PromiseRaceGroup(cancelationRef, group, default, 0, group.Id, false, isResolved); + } + + return new PromiseRaceGroup(cancelationRef, group, isResolved ? _result : promise._result, 0, _groupId, false, true); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If any promise is resolved, the returned promise will be resolved with the value of the promise that resolved first. + /// If no promises are resolved and any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if all promises are canceled, the returned promise will be canceled. + /// + public Promise WaitAsync() + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + if (cancelationRef == null | (group == null & !_isResolved)) + { + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + Internal.ThrowAtLeastOneRaceGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + return Promise.Resolved(_result); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + group.MarkReady(count); + return new Promise(group, group.Id); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseRaceGroup.cs.meta b/Package/Core/PromiseGroups/PromiseRaceGroup.cs.meta new file mode 100644 index 00000000..af818987 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseRaceGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: b142392dc67dde944a17466993e34509 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs b/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs new file mode 100644 index 00000000..fc64fa69 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs @@ -0,0 +1,350 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace Proto.Promises +{ + /// + /// A structured concurrency group used to race promises, incorporating their indices. Waits for the first promise to resolve. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct PromiseRaceWithIndexGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.RacePromiseWithIndexGroupVoid _group; + private readonly int _cancelationId; + private readonly int _count; + private readonly int _index; + private readonly int _winIndex; + private readonly short _groupId; + private readonly bool _cancelOnNonResolved; + + [MethodImpl(Internal.InlineOption)] + private PromiseRaceWithIndexGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.RacePromiseWithIndexGroupVoid group, + int count, int index, int winIndex, short groupId, bool cancelOnNonResolved) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _count = count; + _index = index; + _winIndex = winIndex; + _groupId = groupId; + _cancelOnNonResolved = cancelOnNonResolved; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled if any of the promises in the group are rejected or canceled. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceWithIndexGroup New(out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + => New(CancelationToken.None, out groupCancelationToken, cancelOnNonResolved); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled when is canceled or any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceWithIndexGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseRaceWithIndexGroup(cancelationRef, null, count: 0, index: -1, groupId: 0, winIndex: -1, cancelOnNonResolved: cancelOnNonResolved); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseRaceWithIndexGroup Add(Promise promise) + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var index = _index; + var winIndex = _winIndex; + var cancelOnNonResolved = _cancelOnNonResolved; + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + + checked { ++index; } + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + ++count; + group.AddPromiseWithIndex(promise._ref, promise._id, index); + } + else if (winIndex == -1) + { + winIndex = index; + group.SetResolved(index); + } + return new PromiseRaceWithIndexGroup(cancelationRef, group, count, index, winIndex, group.Id, cancelOnNonResolved); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateRacePromiseWithIndexGroupVoid(cancelationRef, cancelOnNonResolved); + group.AddPromiseWithIndex(promise._ref, promise._id, index); + if (winIndex != -1) + { + group.SetResolved(winIndex); + } + return new PromiseRaceWithIndexGroup(cancelationRef, group, 1, index, winIndex, group.Id, cancelOnNonResolved); + } + + // The promise is already resolved, we need to cancel the group token, + // and catch any exceptions to propagate them out of WaitAsync(). + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + // We already canceled the group token, no need to cancel it again if a promise is non-resolved. + group = Internal.GetOrCreateRacePromiseWithIndexGroupVoid(cancelationRef, false); + group.RecordException(e); + group._cancelationThrew = true; + return new PromiseRaceWithIndexGroup(cancelationRef, group, 0, index, winIndex, group.Id, false); + } + + return new PromiseRaceWithIndexGroup(cancelationRef, group, 0, index, winIndex != -1 ? winIndex : index, _groupId, false); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If any promise is resolved, the returned promise will be resolved with the index of the promise that resolved first. + /// If no promises are resolved and any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if all promises are canceled, the returned promise will be canceled. + /// + public Promise WaitAsync() + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var winIndex = _winIndex; + if (cancelationRef == null | (group == null & winIndex == -1)) + { + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + Internal.ThrowAtLeastOneRaceGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + return Promise.Resolved(winIndex); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + group.MarkReady(count); + return new Promise(group, group.Id); + } + } + + /// + /// A structured concurrency group used to race promises, incorporating their indices. Waits for the first promise to resolve. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + [StructLayout(LayoutKind.Auto)] + public readonly struct PromiseRaceWithIndexGroup + { + private readonly Internal.CancelationRef _cancelationRef; + private readonly Internal.PromiseRefBase.RacePromiseWithIndexGroup _group; + private readonly int _cancelationId; + private readonly int _count; + private readonly int _index; + private readonly int _winIndex; + private readonly short _groupId; + private readonly bool _cancelOnNonResolved; + private readonly T _result; + + [MethodImpl(Internal.InlineOption)] + private PromiseRaceWithIndexGroup(Internal.CancelationRef cancelationRef, Internal.PromiseRefBase.RacePromiseWithIndexGroup group, + int count, int index, short groupId, bool cancelOnNonResolved, int winIndex, in T result) + { + _cancelationRef = cancelationRef; + _group = group; + _cancelationId = cancelationRef.SourceId; + _count = count; + _index = index; + _groupId = groupId; + _cancelOnNonResolved = cancelOnNonResolved; + _winIndex = winIndex; + _result = result; + } + + /// + /// Get a new and the tied to it. + /// + /// The token that will be canceled when any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceWithIndexGroup New(out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + => New(CancelationToken.None, out groupCancelationToken, cancelOnNonResolved); + + /// + /// Get a new that will be canceled when the is canceled, and the tied to it. + /// + /// The token used to cancel the group early. + /// The token that will be canceled when is canceled or any of the promises completed the group. + /// If , the will be canceled when any promise is resolved, canceled, or rejected. + /// Otherwise, the will be canceled when any promise is resolved. + public static PromiseRaceWithIndexGroup New(CancelationToken sourceCancelationToken, out CancelationToken groupCancelationToken, bool cancelOnNonResolved = true) + { + var cancelationRef = Internal.CancelationRef.GetOrCreate(); + cancelationRef.MaybeLinkToken(sourceCancelationToken); + groupCancelationToken = new CancelationToken(cancelationRef, cancelationRef.TokenId); + return new PromiseRaceWithIndexGroup(cancelationRef, null, count: 0, index: -1, groupId: 0, cancelOnNonResolved: cancelOnNonResolved, winIndex: -1, default); + } + + /// + /// Returns a new group with the added to it. + /// + /// The to add to this group. + public PromiseRaceWithIndexGroup Add(Promise promise) + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var index = _index; + var cancelOnNonResolved = _cancelOnNonResolved; + var winIndex = _winIndex; + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + + checked { ++index; } + if (group != null) + { + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + ++count; + group.AddPromiseWithIndex(promise._ref, promise._id, index); + } + else if (winIndex == -1) + { + winIndex = index; + group.SetResolved((index, promise._result)); + } + return new PromiseRaceWithIndexGroup(cancelationRef, group, count, index, group.Id, cancelOnNonResolved, winIndex, default); + } + + if (!cancelationRef.TryIncrementSourceId(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + + if (promise._ref != null) + { + group = Internal.GetOrCreateRacePromiseWithIndexGroup(cancelationRef, cancelOnNonResolved); + group.AddPromiseWithIndex(promise._ref, promise._id, index); + if (winIndex != -1) + { + group.SetResolved((winIndex, _result)); + } + return new PromiseRaceWithIndexGroup(cancelationRef, group, 1, index, group.Id, cancelOnNonResolved, winIndex, default); + } + + // The promise is already resolved, we need to cancel the group token, + // and catch any exceptions to propagate them out of WaitAsync(). + try + { + _cancelationRef.Cancel(); + } + catch (Exception e) + { + // We already canceled the group token, no need to cancel it again if a promise is non-resolved. + group = Internal.GetOrCreateRacePromiseWithIndexGroup(cancelationRef, false); + group.RecordException(e); + group._cancelationThrew = true; + return new PromiseRaceWithIndexGroup(cancelationRef, group, 0, index, group.Id, false, winIndex, default); + } + + return winIndex != -1 + ? new PromiseRaceWithIndexGroup(cancelationRef, group, 0, index, _groupId, false, winIndex, _result) + : new PromiseRaceWithIndexGroup(cancelationRef, group, 0, index, _groupId, false, index, promise._result); + } + + /// + /// Waits asynchronously for all of the promises in this group to complete. + /// If any promise is resolved, the returned promise will be resolved with the index and value of the promise that resolved first. + /// If no promises are resolved and any promise is rejected, the returned promise will be rejected with an containing all of the rejections. + /// Otherwise, if all promises are canceled, the returned promise will be canceled. + /// + public Promise<(int winIndex, T result)> WaitAsync() + { + var cancelationRef = _cancelationRef; + var group = _group; + var count = _count; + var winIndex = _winIndex; + if (cancelationRef == null | (group == null & winIndex == -1)) + { + if (cancelationRef == null) + { + Internal.ThrowInvalidRaceGroup(1); + } + Internal.ThrowAtLeastOneRaceGroup(1); + } + + if (group == null) + { + if (!cancelationRef.TryDispose(_cancelationId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + return Promise.Resolved((winIndex, _result)); + } + + if (!group.TryIncrementId(_groupId)) + { + Internal.ThrowInvalidRaceGroup(1); + } + group.MarkReady(count); + return new Promise<(int, T)>(group, group.Id); + } + } +} \ No newline at end of file diff --git a/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs.meta b/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs.meta new file mode 100644 index 00000000..bafbfd40 --- /dev/null +++ b/Package/Core/PromiseGroups/PromiseRaceWithIndexGroup.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: dc30d8942d6c3e346a98f27b4fc5b58d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/Promises/Exceptions.cs b/Package/Core/Promises/Exceptions.cs index e8d6f5de..ddff5724 100644 --- a/Package/Core/Promises/Exceptions.cs +++ b/Package/Core/Promises/Exceptions.cs @@ -186,21 +186,19 @@ internal CanceledException(string message) : base(message) { } #endif public class RethrowException : Exception, Internal.IRejectionToContainer { -#if !PROMISE_DEBUG + // Old Unity runtime has a bug where stack traces are continually appended to the exception, causing a memory leak and runtime slowdowns. + // To avoid the issue, we only use a singleton in runtimes where the bug is not present. +#if PROMISE_DEBUG || NETSTANDARD2_0 || (UNITY_2018_3_OR_NEWER && !UNITY_2021_2_OR_NEWER) + // Don't re-use instance in DEBUG mode so that we can read its stacktrace on any thread. + internal static RethrowException GetOrCreate() => new RethrowException(); +#else private static readonly RethrowException s_instance = new RethrowException(); + + internal static RethrowException GetOrCreate() => s_instance; #endif protected RethrowException() { } - internal static RethrowException GetOrCreate() - { -#if PROMISE_DEBUG - return new RethrowException(); // Don't re-use instance in DEBUG mode so that we can read its stacktrace on any thread. -#else - return s_instance; -#endif - } - Internal.IRejectContainer Internal.IRejectionToContainer.ToContainer(Internal.ITraceable traceable) { #if PROMISE_DEBUG diff --git a/Package/Core/Promises/Internal/AsyncInternal.cs b/Package/Core/Promises/Internal/AsyncInternal.cs index a4771442..ef3d4d3b 100644 --- a/Package/Core/Promises/Internal/AsyncInternal.cs +++ b/Package/Core/Promises/Internal/AsyncInternal.cs @@ -25,7 +25,7 @@ namespace Proto.Promises partial class Internal { #if NETCOREAPP - private const MethodImplOptions AggressiveOptimizationOption = (MethodImplOptions) 512; + private const MethodImplOptions AggressiveOptimizationOption = MethodImplOptions.AggressiveOptimization; #else private const MethodImplOptions AggressiveOptimizationOption = 0; #endif @@ -36,9 +36,7 @@ partial class PromiseRefBase internal void HookupAwaiter(PromiseRefBase awaiter, short promiseId) { ValidateAwait(awaiter, promiseId); -#if PROMISE_DEBUG - _previous = awaiter; -#endif + this.SetPrevious(awaiter); awaiter.HookupExistingWaiter(promiseId, this); } @@ -139,15 +137,6 @@ internal static void AwaitUnsafeOnCompleted(ref TAwaite CriticalAwaitOverrider.AwaitOnCompleted(ref awaiter, _ref, _ref.MoveNext); #endif } - - partial void SetAwaitedComplete(PromiseRefBase handler); - -#if PROMISE_DEBUG - partial void SetAwaitedComplete(PromiseRefBase handler) - { - _previous = null; - } -#endif } #if !OPTIMIZED_ASYNC_MODE @@ -279,7 +268,7 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) { ThrowIfInPool(this); handler.SetCompletionState(state); - SetAwaitedComplete(handler); + this.SetPrevious(null); _continuer.MoveNext.Invoke(); } } // class AsyncPromiseRef @@ -345,7 +334,6 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) { ThrowIfInPool(this); handler.SetCompletionState(state); - SetAwaitedComplete(handler); ContinueMethod(); } } diff --git a/Package/Core/Promises/Internal/AwaitInternal.cs b/Package/Core/Promises/Internal/AwaitInternal.cs index fb35922c..b31bdc96 100644 --- a/Package/Core/Promises/Internal/AwaitInternal.cs +++ b/Package/Core/Promises/Internal/AwaitInternal.cs @@ -29,7 +29,7 @@ internal ExceptionDispatchInfo GetExceptionDispatchInfo(Promise.State state, sho if (state == Promise.State.Canceled) { MaybeDispose(); - throw CanceledExceptionInternal.GetOrCreate(); + throw Promise.CancelException(); } if (state == Promise.State.Rejected) { diff --git a/Package/Core/Promises/Internal/CallbackHelperInternal.cs b/Package/Core/Promises/Internal/CallbackHelperInternal.cs index 74524dfc..80e481b8 100644 --- a/Package/Core/Promises/Internal/CallbackHelperInternal.cs +++ b/Package/Core/Promises/Internal/CallbackHelperInternal.cs @@ -53,7 +53,9 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegate resolve { try { +#pragma warning disable CS0618 // Type or member is obsolete return resolver.Invoke(CallbackHelperVoid.GetResultFromResolved(resolved)).Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { @@ -249,17 +251,10 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegat { try { - Promise result; - if (resolved._ref == null) - { - result = resolver.Invoke(); - } - else - { - resolved._ref.MaybeMarkAwaitedAndDispose(resolved._id); - result = resolver.Invoke(); - } - return result.Duplicate(); + resolved._ref?.MaybeMarkAwaitedAndDispose(resolved._id); +#pragma warning disable CS0618 // Type or member is obsolete + return resolver.Invoke().Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { @@ -286,7 +281,9 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegat { try { +#pragma warning disable CS0618 // Type or member is obsolete return runner.Invoke().Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { @@ -622,7 +619,9 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegat { try { +#pragma warning disable CS0618 // Type or member is obsolete return resolver.Invoke(CallbackHelperVoid.GetResultFromResolved(resolved)).Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { @@ -839,7 +838,9 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegate resolve try { resolved._ref?.MaybeMarkAwaitedAndDispose(resolved._id); +#pragma warning disable CS0618 // Type or member is obsolete return resolver.Invoke().Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { @@ -866,7 +867,9 @@ private static Promise InvokeCallbackAndAdoptDirect(TDelegate runner) { try { +#pragma warning disable CS0618 // Type or member is obsolete return runner.Invoke().Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } catch (Exception e) { diff --git a/Package/Core/Promises/Internal/CancelInternal.cs b/Package/Core/Promises/Internal/CancelInternal.cs index 7989e7c7..c4a5b348 100644 --- a/Package/Core/Promises/Internal/CancelInternal.cs +++ b/Package/Core/Promises/Internal/CancelInternal.cs @@ -29,6 +29,14 @@ internal override void MaybeReportUnhandledAndDispose(Promise.State state) => MaybeDispose(); } + partial class PromiseRetainer + { + internal override void MaybeReportUnhandledAndDispose(Promise.State state) + // We don't report unhandled rejection here unless none of the waiters suppressed. + // This way we only report it once in case multiple waiters were canceled. + => MaybeDispose(); + } + internal partial struct CancelationHelper { [MethodImpl(InlineOption)] diff --git a/Package/Core/Promises/Internal/DelegateWrappersInternal.cs b/Package/Core/Promises/Internal/DelegateWrappersInternal.cs index 989bbed2..58f99ccb 100644 --- a/Package/Core/Promises/Internal/DelegateWrappersInternal.cs +++ b/Package/Core/Promises/Internal/DelegateWrappersInternal.cs @@ -606,21 +606,21 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } [MethodImpl(InlineOption)] void IDelegateRunPromise.Invoke(PromiseRefBase owner) { Promise result = Invoke(); - owner.WaitFor(result, null); + owner.WaitFor(result); } } @@ -652,21 +652,21 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } [MethodImpl(InlineOption)] void IDelegateRunPromise.Invoke(PromiseRefBase owner) { Promise result = Invoke(); - owner.WaitFor(result, null); + owner.WaitFor(result); } } @@ -699,7 +699,7 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom TArg arg = handler.GetResult(); handler.MaybeDispose(); Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) @@ -708,7 +708,7 @@ void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContai if (rejectContainer.TryGetValue(out TArg arg)) { Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } else { @@ -747,7 +747,7 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom TArg arg = handler.GetResult(); handler.MaybeDispose(); Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) @@ -756,7 +756,7 @@ void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContai if (rejectContainer.TryGetValue(out TArg arg)) { Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } else { @@ -926,7 +926,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -963,7 +963,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1000,7 +1000,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1037,7 +1037,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1447,21 +1447,21 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } [MethodImpl(InlineOption)] void IDelegateRunPromise.Invoke(PromiseRefBase owner) { Promise result = Invoke(); - owner.WaitFor(result, null); + owner.WaitFor(result); } } @@ -1495,21 +1495,21 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) { handler.MaybeDispose(); Promise result = Invoke(); - owner.WaitFor(result, handler); + owner.WaitFor(result); } [MethodImpl(InlineOption)] void IDelegateRunPromise.Invoke(PromiseRefBase owner) { Promise result = Invoke(); - owner.WaitFor(result, null); + owner.WaitFor(result); } } @@ -1544,7 +1544,7 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom TArg arg = handler.GetResult(); handler.MaybeDispose(); Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) @@ -1553,7 +1553,7 @@ void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContai if (rejectContainer.TryGetValue(out TArg arg)) { Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } else { @@ -1594,7 +1594,7 @@ void IDelegateResolveOrCancelPromise.InvokeResolver(PromiseRefBase handler, Prom TArg arg = handler.GetResult(); handler.MaybeDispose(); Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContainer rejectContainer, PromiseRefBase owner) @@ -1603,7 +1603,7 @@ void IDelegateRejectPromise.InvokeRejecter(PromiseRefBase handler, IRejectContai if (rejectContainer.TryGetValue(out TArg arg)) { Promise result = Invoke(arg); - owner.WaitFor(result, handler); + owner.WaitFor(result); } else { @@ -1785,7 +1785,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1824,7 +1824,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1863,7 +1863,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } @@ -1902,7 +1902,7 @@ public void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Pro var resultContainer = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); handler.MaybeDispose(); Promise result = Invoke(resultContainer); - owner.WaitFor(result, handler); + owner.WaitFor(result); } } diff --git a/Package/Core/Promises/Internal/EachInternal.cs b/Package/Core/Promises/Internal/EachInternal.cs new file mode 100644 index 00000000..1b0339a1 --- /dev/null +++ b/Package/Core/Promises/Internal/EachInternal.cs @@ -0,0 +1,288 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +#pragma warning disable IDE0090 // Use 'new(...)' + +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Threading; + +namespace Proto.Promises +{ + partial class Internal + { + partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class PromiseEachAsyncEnumerable : AsyncEnumerableBase, ICancelable + { + private static GetResultDelegate s_getResult; + + private CancelationRegistration _cancelationRegistration; + // This must not be readonly. + private PoolBackedQueue _queue; + private int _remaining; + private int _retainCount; + private bool _isMoveNextAsyncPending; + private bool _isCanceled; + + private PromiseEachAsyncEnumerable() { } + + [MethodImpl(InlineOption)] + private static PromiseEachAsyncEnumerable GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new PromiseEachAsyncEnumerable() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static PromiseEachAsyncEnumerable GetOrCreate(GetResultDelegate getResultDelegate) + { + s_getResult = getResultDelegate; + + var enumerable = GetOrCreate(); + enumerable.Reset(); + return enumerable; + } + + new private void Reset() + { + base.Reset(); + _remaining = 0; + // 1 retain for DisposeAsync, and 1 retain for cancelation registration. + _retainCount = 2; + _isCanceled = false; + _queue = new PoolBackedQueue(0); + } + + [MethodImpl(InlineOption)] + internal void AddResult(in TResult result) + { + ++_remaining; + lock (this) + { + _queue.Enqueue(result); + } + } + + internal void AddPromise(PromiseRefBase promise, short id) + { + AddPending(promise); + + ++_remaining; + InterlockedAddWithUnsignedOverflowCheck(ref _retainCount, 1); + promise.HookupNewWaiter(id, this); + } + + new private void Dispose() + { + ValidateNoPending(); + +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE + SetCompletionState(Promise.State.Resolved); +#endif + // MoveNextAsync/DisposeAsync may have completed synchronously, in which case this will never have had a waiter added to it. + // So we need to mark it awaited to prevent the finalizer from reporting it as not awaited. + WasAwaitedOrForgotten = true; + base.Dispose(); + _disposed = true; + _current = default; + _queue.Dispose(); + _queue = default; + ObjectPool.MaybeRepool(this); + } + + internal override Promise DisposeAsync(int id) + { + int newId = id + 2; + int oldId = Interlocked.CompareExchange(ref _enumerableId, newId, id); + if (oldId != id) + { + if (oldId == id + 1) + { + throw new InvalidOperationException("AsyncEnumerator.DisposeAsync: the previous MoveNextAsync operation is still pending.", GetFormattedStacktrace(2)); + } + // IAsyncDisposable.DisposeAsync must not throw if it's called multiple times, according to MSDN documentation. + return Promise.Resolved(); + } + + // Same as calling MaybeDispose twice, but without the extra interlocked and branch. + int releaseCount = TryUnregisterAndIsNotCanceling(ref _cancelationRegistration) & !_isCanceled ? -2 : -1; + _cancelationRegistration = default; + if (InterlockedAddWithUnsignedOverflowCheck(ref _retainCount, releaseCount) == 0) + { + Dispose(); + } + return Promise.Resolved(); + } + + internal override void MaybeDispose() + { + if (InterlockedAddWithUnsignedOverflowCheck(ref _retainCount, -1) == 0) + { + Dispose(); + } + } + + internal override Promise MoveNextAsync(int id) + { + if (Interlocked.CompareExchange(ref _enumerableId, id + 1, id) != id) + { + throw new InvalidOperationException("AsyncEnumerable.MoveNextAsync: instance is not valid, or the previous MoveNextAsync operation is still pending.", GetFormattedStacktrace(2)); + } + + if (!_isStarted) + { + _isStarted = true; + _cancelationToken.TryRegisterWithoutImmediateInvoke(this, out _cancelationRegistration, out bool alreadyCanceled); + if (alreadyCanceled) + { + _enumerableId = id; + return Promise.Canceled(); + } + } + else if (_remaining == 0) + { + _enumerableId = id; + return Promise.Resolved(false); + } + else if (_cancelationToken.IsCancelationRequested | _isCanceled) + { + _enumerableId = id; + return Promise.Canceled(); + } + --_remaining; + + // Reset this before entering the lock so that we're not spending extra time inside the lock. + ResetForNextAwait(); + bool pending; + lock (this) + { + _isMoveNextAsyncPending = pending = !_queue.TryDequeue(out _current); + } + if (pending) + { + return new Promise(this, Id); + } + _enumerableId = id; + return Promise.Resolved(true); + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + RemoveComplete(handler); + + handler.SetCompletionState(state); + TResult result = default; + s_getResult.Invoke(handler, 0, ref result); + handler.MaybeDispose(); + + lock (this) + { + if (_isMoveNextAsyncPending) + { + // MoveNextAsync is pending, so we can skip the queue and just set the current directly. + _isMoveNextAsyncPending = false; + goto HandleNext; + } + _queue.Enqueue(result); + } + MaybeDispose(); + return; + + HandleNext: + --_enumerableId; + _result = true; + _current = result; + HandleNextInternal(Promise.State.Resolved); + } + + void ICancelable.Cancel() + { + _isCanceled = true; + + lock (this) + { + if (_isMoveNextAsyncPending) + { + _isMoveNextAsyncPending = false; + goto HandleNext; + } + } + MaybeDispose(); + return; + + HandleNext: + --_enumerableId; + _result = false; + HandleNextInternal(Promise.State.Canceled); + } + + partial void AddPending(PromiseRefBase pendingPromise); + partial void RemoveComplete(PromiseRefBase completePromise); + partial void ValidateNoPending(); + } + +#if PROMISE_DEBUG + partial class PromiseEachAsyncEnumerable + { + private readonly HashSet _pendingPromises = new HashSet(); + + protected override void BorrowPreviousPromises(Stack borrower) + { + lock (_pendingPromises) + { + foreach (var promiseRef in _pendingPromises) + { + borrower.Push(promiseRef); + } + } + } + + partial void ValidateNoPending() + { + lock (_pendingPromises) + { + if (_pendingPromises.Count != 0) + { + throw new System.InvalidOperationException("PromiseEachAsyncEnumerable disposed with pending promises."); + } + } + } + + partial void AddPending(PromiseRefBase pendingPromise) + { + lock (_pendingPromises) + { + _pendingPromises.Add(pendingPromise); + } + } + + partial void RemoveComplete(PromiseRefBase completePromise) + { + lock (_pendingPromises) + { + _pendingPromises.Remove(completePromise); + } + } + } +#endif // PROMISE_DEBUG + } // class PromiseRefBase + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.PromiseEachAsyncEnumerable.ResultContainer> GetOrCreatePromiseEachAsyncEnumerable() + => PromiseRefBase.PromiseEachAsyncEnumerable.ResultContainer>.GetOrCreate(Promise.MergeResultFuncs.GetMergeResult()); + + [MethodImpl(InlineOption)] + internal static PromiseRefBase.PromiseEachAsyncEnumerable GetOrCreatePromiseEachAsyncEnumerableVoid() + => PromiseRefBase.PromiseEachAsyncEnumerable.GetOrCreate(Promise.MergeResultFuncs.GetMergeResultVoid()); + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/Promises/Internal/EachInternal.cs.meta b/Package/Core/Promises/Internal/EachInternal.cs.meta new file mode 100644 index 00000000..e657aef9 --- /dev/null +++ b/Package/Core/Promises/Internal/EachInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 5f97e7f3ce1696b4dbe8fcc1e61a1f06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/Promises/Internal/MergeInternal.cs b/Package/Core/Promises/Internal/MergeInternal.cs index ecf59b0a..49ad3c10 100644 --- a/Package/Core/Promises/Internal/MergeInternal.cs +++ b/Package/Core/Promises/Internal/MergeInternal.cs @@ -28,21 +28,8 @@ internal readonly unsafe struct GetResultDelegate [MethodImpl(InlineOption)] internal void Invoke(PromiseRefBase handler, int index, ref TResult result) => _ptr(handler, index, ref result); } - - internal readonly unsafe struct GetResultContainerDelegate - { - private readonly delegate* _ptr; - - [MethodImpl(InlineOption)] - internal GetResultContainerDelegate(delegate* ptr) => _ptr = ptr; - - [MethodImpl(InlineOption)] - internal void Invoke(PromiseRefBase handler, IRejectContainer rejectContainer, Promise.State state, int index, ref TResult result) => _ptr(handler, rejectContainer, state, index, ref result); - } #else internal delegate void GetResultDelegate(PromiseRefBase handler, int index, ref TResult result); - - internal delegate void GetResultContainerDelegate(PromiseRefBase handler, IRejectContainer rejectContainer, Promise.State state, int index, ref TResult result); #endif [MethodImpl(InlineOption)] @@ -81,7 +68,7 @@ internal static void PrepareForMerge(Promise promise, ref T value [MethodImpl(InlineOption)] internal static void PrepareForMergeSettled(Promise promise, in TResult result, ref uint pendingCount, int index, - ref PromiseRefBase.MergeSettledPromise mergePromise, GetResultContainerDelegate getResultDelegate) + ref PromiseRefBase.MergeSettledPromise mergePromise, GetResultDelegate getResultDelegate) { if (promise._ref != null) { @@ -96,7 +83,7 @@ internal static void PrepareForMergeSettled(Promise promise, in TResult [MethodImpl(InlineOption)] internal static void PrepareForMergeSettled(Promise promise, ref Promise.ResultContainer value, in TResult result, ref uint pendingCount, int index, - ref PromiseRefBase.MergeSettledPromise mergePromise, GetResultContainerDelegate getResultDelegate) + ref PromiseRefBase.MergeSettledPromise mergePromise, GetResultDelegate getResultDelegate) { if (promise._ref == null) { @@ -296,6 +283,9 @@ internal void MarkReady(uint totalWaiters) => MarkReady(totalWaiters, ref _waitCount, Promise.State.Resolved); } +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif internal sealed partial class MergePromise : MergePromiseBase { private static GetResultDelegate s_getResult; @@ -419,9 +409,12 @@ internal static MergePromise> GetOrCreateAllPromise( uint waitCount) => MergePromise>.GetOrCreateAll(value, getResultFunc, passthroughs, unchecked((int) waitCount)); +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif internal sealed partial class MergeSettledPromise : MergePromiseBase { - private static GetResultContainerDelegate s_getResult; + private static GetResultDelegate s_getResult; [MethodImpl(InlineOption)] private static MergeSettledPromise GetOrCreate() @@ -433,7 +426,7 @@ private static MergeSettledPromise GetOrCreate() } [MethodImpl(InlineOption)] - internal static MergeSettledPromise GetOrCreate(in TResult value, GetResultContainerDelegate getResultFunc) + internal static MergeSettledPromise GetOrCreate(in TResult value, GetResultDelegate getResultFunc) { s_getResult = getResultFunc; var promise = GetOrCreate(); @@ -445,7 +438,7 @@ internal static MergeSettledPromise GetOrCreate(in TResult value, GetRe [MethodImpl(InlineOption)] internal static MergeSettledPromise GetOrCreateAll( TResult value, - GetResultContainerDelegate getResultFunc, + GetResultDelegate getResultFunc, ValueLinkedStack passthroughs, int waitCount) { @@ -474,8 +467,7 @@ internal override void MaybeDispose() internal override void Handle(PromiseRefBase handler, Promise.State state, int index) { handler.SetCompletionState(state); - _rejectContainer = handler._rejectContainer; - s_getResult.Invoke(handler, _rejectContainer, state, index, ref _result); + s_getResult.Invoke(handler, index, ref _result); handler.SuppressRejection = true; handler.MaybeDispose(); if (RemoveWaiterAndGetIsComplete(handler)) @@ -489,13 +481,13 @@ internal override void Handle(PromiseRefBase handler, Promise.State state, int i } [MethodImpl(InlineOption)] - internal static MergeSettledPromise GetOrCreateMergeSettledPromise(in TResult value, GetResultContainerDelegate getResultFunc) + internal static MergeSettledPromise GetOrCreateMergeSettledPromise(in TResult value, GetResultDelegate getResultFunc) => MergeSettledPromise.GetOrCreate(value, getResultFunc); [MethodImpl(InlineOption)] internal static MergeSettledPromise> GetOrCreateAllSettledPromise( IList value, - GetResultContainerDelegate> getResultFunc, + GetResultDelegate> getResultFunc, ValueLinkedStack passthroughs, uint waitCount) => MergeSettledPromise>.GetOrCreateAll(value, getResultFunc, passthroughs, unchecked((int) waitCount)); diff --git a/Package/Core/Promises/Internal/ParallelAsyncInternal.cs b/Package/Core/Promises/Internal/ParallelAsyncInternal.cs index b8ae645c..e140ea9e 100644 --- a/Package/Core/Promises/Internal/ParallelAsyncInternal.cs +++ b/Package/Core/Promises/Internal/ParallelAsyncInternal.cs @@ -392,7 +392,7 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) if (state == Promise.State.Rejected) { // Record the failure. The last worker to complete will propagate exceptions as is appropriate to the top-level promise. - RecordRejection(rejectContainer); + RecordException(rejectContainer.GetValueAsException()); } if (isMoveNextAsyncContinuation) { @@ -404,15 +404,6 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) } } - private void RecordRejection(IRejectContainer rejectContainer) - { - var container = rejectContainer; - var exception = container.Value as Exception - // If the reason was not an exception, get the reason wrapped in an exception. - ?? container.GetExceptionDispatchInfo().SourceException; - RecordException(exception); - } - private void RecordException(Exception e) { lock (this) @@ -520,7 +511,7 @@ internal override void Handle(PromiseRefBase handler, Promise.State state, int i handler.SetCompletionState(state); if (state == Promise.State.Rejected) { - RecordRejection(handler._rejectContainer); + RecordException(handler._rejectContainer.GetValueAsException()); handler.SuppressRejection = true; } handler.MaybeDispose(); diff --git a/Package/Core/Promises/Internal/ParallelInternal.cs b/Package/Core/Promises/Internal/ParallelInternal.cs index a50343dc..ef6eb723 100644 --- a/Package/Core/Promises/Internal/ParallelInternal.cs +++ b/Package/Core/Promises/Internal/ParallelInternal.cs @@ -396,11 +396,7 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) { CancelWorkers(); // Record the failure. The last worker to complete will propagate exceptions as is appropriate to the top-level promise. - var container = rejectContainer; - var exception = container.Value as Exception - // If the reason was not an exception, get the reason wrapped in an exception. - ?? container.GetExceptionDispatchInfo().SourceException; - RecordException(exception); + RecordException(rejectContainer.GetValueAsException()); MaybeComplete(); } } diff --git a/Package/Core/Promises/Internal/PromiseFieldsInternal.cs b/Package/Core/Promises/Internal/PromiseFieldsInternal.cs index 617c1f4b..050631ae 100644 --- a/Package/Core/Promises/Internal/PromiseFieldsInternal.cs +++ b/Package/Core/Promises/Internal/PromiseFieldsInternal.cs @@ -15,8 +15,11 @@ #define OPTIMIZED_ASYNC_MODE #endif +#pragma warning disable IDE0090 // Use 'new(...)' + using Proto.Promises.Collections; using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Threading; @@ -90,13 +93,6 @@ partial class PromiseSynchronousWaiter : HandleablePromiseBase partial class PromiseRefBase : HandleablePromiseBase { - internal enum WaitState : byte - { - First, - SettingSecond, - Second, - } - #if PROMISE_DEBUG CausalityTrace ITraceable.Trace { get; set; } internal PromiseRefBase _previous; // Used to detect circular awaits. @@ -155,6 +151,12 @@ partial class PromiseMultiAwait : PromiseRef private int _retainCounter; } + partial class PromiseRetainer : PromiseRef + { + private TempCollectionBuilder _nextBranches; + private int _retainCounter; + } + partial class PromiseWaitPromise : PromiseSingleAwait { } @@ -164,7 +166,7 @@ partial class DeferredPromiseBase : PromiseSingleAwait, IDefer protected int _deferredId = 1; // Start with Id 1 instead of 0 to reduce risk of false positives. } -#region Non-cancelable Promises + #region Non-cancelable Promises partial class DeferredPromise : DeferredPromiseBase { } @@ -239,9 +241,9 @@ partial class PromiseCancelPromise : PromiseWaitPromise : PromiseWaitPr internal CancelationHelper _cancelationHelper; private TCanceler _canceler; } -#endregion + #endregion -#region Multi Promises + #region Multi Promises partial class MultiHandleablePromiseBase : PromiseSingleAwait { protected int _retainCounter; @@ -320,7 +322,7 @@ partial class RacePromise : MultiHandleablePromiseBase { } - partial class RacePromiseWithIndex : RacePromise> + partial class RacePromiseWithIndex : RacePromise<(int, TResult)> { } @@ -329,7 +331,7 @@ partial class FirstPromise : RacePromise protected int _waitCount; // int for Interlocked since it doesn't support uint on older runtimes. } - partial class FirstPromiseWithIndex : FirstPromise> + partial class FirstPromiseWithIndex : FirstPromise<(int, TResult)> { } @@ -362,6 +364,72 @@ partial class PromisePassThroughForAll : PromisePassThrough #endif private short _id; } + + partial class PromisePassThroughForMergeGroup : PromisePassThrough + { +#if !(PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE) + private PromiseRefBase _owner; +#endif + } + + partial class PromiseGroupBase : PromiseSingleAwait + { + internal List _exceptions; + protected CancelationRef _cancelationRef; // Store the reference directly instead of CancelationSource struct to reduce memory. + private int _cancelationId; + private int _waitCount; // int for Interlocked since it doesn't support uint on older runtimes. + protected Promise.State _completeState; + } + + partial class MergePromiseGroupBase : PromiseGroupBase + { + internal ValueLinkedStack _completedPassThroughs = new ValueLinkedStack(); + } + + partial class MergePromiseGroupVoid : MergePromiseGroupBase + { + } + + partial class MergePromiseGroup : PromiseSingleAwait + { + private bool _isExtended; + } + + partial class MergePromiseResultsGroup : PromiseSingleAwait + { + private bool _isExtended; + } + + partial class AllPromiseGroup : MergePromiseGroupBase> + { + } + + partial class AllPromiseResultsGroupVoid : MergePromiseGroupBase> + { + } + + partial class AllPromiseResultsGroup : MergePromiseGroupBase.ResultContainer>> + { + } + + partial class RacePromiseGroupBase : PromiseGroupBase + { + protected int _isResolved; // Flag used to indicate that the promise has already been resolved. int for Interlocked. + protected bool _cancelOnNonResolved; + internal bool _cancelationThrew; + } + + partial class RacePromiseGroup : RacePromiseGroupBase + { + } + + partial class RacePromiseWithIndexGroupVoid : RacePromiseGroupBase + { + } + + partial class RacePromiseWithIndexGroup : RacePromiseGroupBase<(int, TResult)> + { + } #endregion partial class AsyncPromiseRef : PromiseSingleAwait @@ -412,7 +480,9 @@ partial struct AsyncPromiseMethodBuilder #if OPTIMIZED_ASYNC_MODE private T _result; #else - private T _result => default; +#pragma warning disable IDE1006 // Naming Styles + private static T _result => default; +#pragma warning restore IDE1006 // Naming Styles #endif } } diff --git a/Package/Core/Promises/Internal/PromiseInternal.cs b/Package/Core/Promises/Internal/PromiseInternal.cs index 3287353d..0aa414cf 100644 --- a/Package/Core/Promises/Internal/PromiseInternal.cs +++ b/Package/Core/Promises/Internal/PromiseInternal.cs @@ -93,6 +93,16 @@ public override System.Threading.Tasks.Sources.ValueTaskSourceStatus GetStatus(s } } + partial class PromiseRetainer + { + public override System.Threading.Tasks.Sources.ValueTaskSourceStatus GetStatus(short token) + { + ValidateId(token, this, 2); + ThrowIfInPool(this); + return (System.Threading.Tasks.Sources.ValueTaskSourceStatus) State; + } + } + partial class CanceledPromiseSentinel { public override System.Threading.Tasks.Sources.ValueTaskSourceStatus GetStatus(short token) @@ -295,8 +305,12 @@ internal void HandleSelf(PromiseRefBase handler, Promise.State state) } } + // TODO: If/when we remove `Promise.Preserve()`, we can also remove this MaybeMarkAwaitedAndDispose method, and just use Forget instead. internal abstract void MaybeMarkAwaitedAndDispose(short promiseId); internal abstract void MaybeDispose(); + // TODO: We can remove this virtual GetIsCompleted call and make it a simple State check instead. + // Doing so will require removing `Promise.Preserve()` API, so it will need a major version update. + // We will also need to move the `_state = Promise.State.Pending` from ResetWithoutStacktrace() to Dispose(). internal abstract bool GetIsCompleted(short promiseId); internal abstract void Forget(short promiseId); internal abstract PromiseRefBase GetDuplicate(short promiseId); @@ -383,10 +397,8 @@ private void Dispose() protected void IncrementPromiseIdAndClearPrevious() { IncrementPromiseId(); -#if PROMISE_DEBUG - _previous = null; -#endif _rejectContainer = null; + this.SetPrevious(null); } [MethodImpl(InlineOption)] @@ -401,23 +413,15 @@ private void IncrementPromiseId() internal TPromise HookupCancelablePromise(TPromise promise, short promiseId, CancelationToken cancelationToken, ref CancelationHelper cancelationHelper) where TPromise : PromiseRefBase, ICancelable { - promise.SetNewPrevious(this); + promise.SetPrevious(this); cancelationHelper.Register(cancelationToken, promise); // Very important, must register after promise is fully setup. HookupNewWaiter(promiseId, promise); return promise; } - [MethodImpl(InlineOption)] - internal void SetNewPrevious(PromiseRefBase previous) - { -#if PROMISE_DEBUG - _previous = previous; -#endif - } - internal void HookupNewPromise(short promiseId, PromiseRefBase newPromise) { - newPromise.SetNewPrevious(this); + newPromise.SetPrevious(this); HookupNewWaiter(promiseId, newPromise); } @@ -761,7 +765,7 @@ internal override void Forget(short promiseId) { if (!GetIsValid(promiseId)) { - throw new InvalidOperationException("Cannot forget a promise more than once.", GetFormattedStacktrace(3)); + throw new InvalidOperationException("Cannot forget a promise more than once.", GetFormattedStacktrace(2)); } WasAwaitedOrForgotten = true; MaybeDispose(); @@ -1276,19 +1280,19 @@ internal override void Handle(PromiseRefBase handler, Promise.State state) } [MethodImpl(InlineOption)] - internal void WaitFor(Promise other, PromiseRefBase handler) + internal void WaitFor(Promise other) { ThrowIfInPool(this); ValidateReturn(other); - this.UnsafeAs>().WaitFor(other._ref, other._id, handler); + this.UnsafeAs>().WaitFor(other._ref, other._id); } [MethodImpl(InlineOption)] - internal void WaitFor(in Promise other, PromiseRefBase handler) + internal void WaitFor(in Promise other) { ThrowIfInPool(this); ValidateReturn(other); - this.UnsafeAs>().WaitFor(other._ref, other._result, other._id, handler); + this.UnsafeAs>().WaitFor(other._ref, other._result, other._id); } // This is only used in PromiseWaitPromise, but we pulled it out to prevent excess generated generic interface types. @@ -1298,20 +1302,6 @@ protected interface IWaitForCompleteHandler void HandleNull(); } - // This is rare, only happens when the promise already completed (usually an already completed promise is not backed by a reference), or if a promise is incorrectly awaited twice. - [MethodImpl(MethodImplOptions.NoInlining)] - private static void VerifyAndHandleSelf(PromiseRefBase other, PromiseRefBase promiseSingleAwait, TCompleteHandler completeHandler) - where TCompleteHandler : IWaitForCompleteHandler - { - if (!VerifyWaiter(promiseSingleAwait)) - { - throw new InvalidReturnException("Cannot await or forget a forgotten promise or a non-preserved promise more than once.", string.Empty); - } - - other.WaitUntilStateIsNotPending(); - completeHandler.HandleHookup(other); - } - #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif @@ -1340,61 +1330,64 @@ void IWaitForCompleteHandler.HandleNull() } [MethodImpl(InlineOption)] - internal void WaitFor(PromiseRefBase other, short id, PromiseRefBase handler) - => WaitFor(other, id, handler, new DefaultCompleteHandler(this)); + internal void WaitFor(PromiseRefBase other, short id) + => WaitFor(other, id, new DefaultCompleteHandler(this)); [MethodImpl(InlineOption)] - internal void WaitFor(PromiseRefBase other, in TResult maybeResult, short id, PromiseRefBase handler) - => WaitFor(other, maybeResult, id, handler, new DefaultCompleteHandler(this)); + internal void WaitFor(PromiseRefBase other, in TResult maybeResult, short id) + => WaitFor(other, maybeResult, id, new DefaultCompleteHandler(this)); [MethodImpl(InlineOption)] - protected void WaitFor(PromiseRefBase other, short id, PromiseRefBase handler, TCompleteHandler completeHandler) + protected void WaitFor(PromiseRefBase other, short id, TCompleteHandler completeHandler) where TCompleteHandler : IWaitForCompleteHandler { if (other == null) { - SetSecondPrevious(null, handler); + this.SetPrevious(null); completeHandler.HandleNull(); return; } - SetSecondPreviousAndWaitFor(other, id, handler, completeHandler); + SetSecondPreviousAndWaitFor(other, id, completeHandler); } [MethodImpl(InlineOption)] - protected void WaitFor(PromiseRefBase other, in TResult maybeResult, short id, PromiseRefBase handler, TCompleteHandler completeHandler) + protected void WaitFor(PromiseRefBase other, in TResult maybeResult, short id, TCompleteHandler completeHandler) where TCompleteHandler : IWaitForCompleteHandler { if (other == null) { _result = maybeResult; - SetSecondPrevious(null, handler); + this.SetPrevious(null); completeHandler.HandleNull(); return; } - SetSecondPreviousAndWaitFor(other, id, handler, completeHandler); + SetSecondPreviousAndWaitFor(other, id, completeHandler); } - [MethodImpl(InlineOption)] - internal void SetSecondPreviousAndWaitFor(PromiseRefBase secondPrevious, short id, PromiseRefBase handler) - => SetSecondPreviousAndWaitFor(secondPrevious, id, handler, new DefaultCompleteHandler(this)); - - private void SetSecondPreviousAndWaitFor(PromiseRefBase secondPrevious, short id, PromiseRefBase handler, TCompleteHandler completeHandler) + private void SetSecondPreviousAndWaitFor(PromiseRefBase secondPrevious, short id, TCompleteHandler completeHandler) where TCompleteHandler : IWaitForCompleteHandler { PromiseRefBase promiseSingleAwait = secondPrevious.AddWaiter(id, this, out var previousWaiter); - SetSecondPrevious(secondPrevious, handler); + this.SetPrevious(secondPrevious); if (previousWaiter != PendingAwaitSentinel.s_instance) { VerifyAndHandleSelf(secondPrevious, promiseSingleAwait, completeHandler); } } - partial void SetSecondPrevious(PromiseRefBase secondPrevious, PromiseRefBase handler); + // This is rare, only happens when the promise already completed (usually an already completed promise is not backed by a reference), or if a promise is incorrectly awaited twice. + [MethodImpl(MethodImplOptions.NoInlining)] + private static void VerifyAndHandleSelf(PromiseRefBase other, PromiseRefBase promiseSingleAwait, TCompleteHandler completeHandler) + where TCompleteHandler : IWaitForCompleteHandler + { + if (!VerifyWaiter(promiseSingleAwait)) + { + throw new InvalidReturnException("Cannot await or forget a forgotten promise or a non-preserved promise more than once.", string.Empty); + } -#if PROMISE_DEBUG - partial void SetSecondPrevious(PromiseRefBase secondPrevious, PromiseRefBase handler) - => _previous = secondPrevious; -#endif + other.WaitUntilStateIsNotPending(); + completeHandler.HandleHookup(other); + } } // IDelegate to reduce the amount of classes I would have to write (Composition Over Inheritance). @@ -1839,7 +1832,7 @@ protected override void Execute(PromiseRefBase handler, Promise.State state, ref } // Store the state until the returned promise is complete. _previousState = state; - WaitFor(result._ref, result._id, handler, new CompleteHandler(this)); + WaitFor(result._ref, result._id, new CompleteHandler(this)); } private void HandleFromReturnedPromise(PromiseRefBase handler, Promise.State state) @@ -1997,7 +1990,7 @@ internal partial class PromisePassThrough : HandleablePromiseBase if (!_disposed) { // For debugging. This should never happen. - string message = $"A PromisePassThrough was garbage collected without it being released. _index: {_index}, _owner: {_owner}, _next: {_next}"; + string message = $"A {GetType()} was garbage collected without it being released. _index: {_index}, _owner: {_owner}, _next: {_next}"; ReportRejection(new UnreleasedObjectException(message), _owner); } } diff --git a/Package/Core/Promises/Internal/PromiseRetainerInternal.cs b/Package/Core/Promises/Internal/PromiseRetainerInternal.cs new file mode 100644 index 00000000..75bfa894 --- /dev/null +++ b/Package/Core/Promises/Internal/PromiseRetainerInternal.cs @@ -0,0 +1,229 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +#pragma warning disable IDE0016 // Use 'throw' expression + +using Proto.Promises.Collections; +using System; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + partial class Internal + { + internal abstract partial class PromiseRefBase + { +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + internal sealed partial class PromiseRetainer : PromiseRef + { + private PromiseRetainer() { } + + ~PromiseRetainer() + { + if (!WasAwaitedOrForgotten) + { + WasAwaitedOrForgotten = true; // Stop base finalizer from adding an extra exception. + string message = "A retained Promise's resources were garbage collected without it being disposed."; + ReportRejection(new UnreleasedObjectException(message), this); + } + else if (_retainCounter != 0 & State != Promise.State.Pending) + { + string message = "A Promise from a retainer was not awaited or forgotten."; + ReportRejection(new UnreleasedObjectException(message), this); + } + } + + [MethodImpl(InlineOption)] + new private void Reset() + { + _retainCounter = 2; // 1 for dispose, 1 for completion. + base.Reset(); + } + + [MethodImpl(InlineOption)] + private static PromiseRetainer GetOrCreate() + { + var obj = ObjectPool.TryTakeOrInvalid>(); + return obj == InvalidAwaitSentinel.s_instance + ? new PromiseRetainer() + : obj.UnsafeAs>(); + } + + [MethodImpl(InlineOption)] + internal static PromiseRetainer GetOrCreateAndHookup(PromiseRefBase previous, short id) + { + var promise = GetOrCreate(); + promise.Reset(); + previous.HookupNewPromise(id, promise); + // We create the temp collection after we hook up in case the operation is invalid. + promise._nextBranches = new TempCollectionBuilder(0); + return promise; + } + + [MethodImpl(InlineOption)] + private void Retain() + => InterlockedAddWithUnsignedOverflowCheck(ref _retainCounter, 1); + + internal override void MaybeDispose() + { + if (InterlockedAddWithUnsignedOverflowCheck(ref _retainCounter, -1) != 0) + { + return; + } +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE + if (!WasAwaitedOrForgotten) + { + throw new System.InvalidOperationException("PromiseRetainer was MaybeDisposed completely without being properly disposed."); + } +#endif + _nextBranches.Dispose(); + // Rejection maybe wasn't caught. + // We handle this directly here because we don't add the PromiseForgetSentinel to this type when it is disposed. + MaybeReportUnhandledRejection(_rejectContainer, State); + Dispose(); + ObjectPool.MaybeRepool(this); + } + + internal void Dispose(short promiseId) + { + lock (this) + { + if (promiseId != Id | WasAwaitedOrForgotten) + { + throw new ObjectDisposedException("The promise retainer was already disposed.", GetFormattedStacktrace(2)); + } + ThrowIfInPool(this); + + WasAwaitedOrForgotten = true; + } + MaybeDispose(); + } + + internal override bool GetIsCompleted(short promiseId) + { + ValidateId(promiseId, this, 2); + ThrowIfInPool(this); + return State != Promise.State.Pending; + } + + internal override PromiseRefBase GetDuplicate(short promiseId) + { + ValidateId(promiseId, this, 2); + ThrowIfInPool(this); + return this; + } + + internal override PromiseRef GetDuplicateT(short promiseId) + { + ValidateId(promiseId, this, 2); + ThrowIfInPool(this); + return this; + } + + [MethodImpl(InlineOption)] + internal override bool GetIsValid(short promiseId) + => promiseId == Id; + + internal override void Forget(short promiseId) + => MaybeMarkAwaitedAndDispose(promiseId); + + internal override void MaybeMarkAwaitedAndDispose(short promiseId) + { + ValidateId(promiseId, this, 2); + ThrowIfInPool(this); + MaybeDispose(); + } + + internal override PromiseRefBase AddWaiter(short promiseId, HandleablePromiseBase waiter, out HandleablePromiseBase previousWaiter) + { + lock (this) + { + if (promiseId != Id) + { + previousWaiter = InvalidAwaitSentinel.s_instance; + return InvalidAwaitSentinel.s_instance; + } + ThrowIfInPool(this); + + if (State == Promise.State.Pending) + { + _nextBranches.Add(waiter); + previousWaiter = PendingAwaitSentinel.s_instance; + return null; + } + } + previousWaiter = waiter; + return null; + } + + internal override void Handle(PromiseRefBase handler, Promise.State state) + { + ThrowIfInPool(this); + handler.SetCompletionState(state); + _rejectContainer = handler._rejectContainer; + handler.SuppressRejection = true; + _result = handler.GetResult(); + SetCompletionState(state); + handler.MaybeDispose(); + + TempCollectionBuilder branches; + lock (this) + { + branches = _nextBranches; + } + for (int i = 0, max = branches._count; i < max; ++i) + { + branches[i].Handle(this, state); + } + MaybeDispose(); + } + + internal Promise WaitAsync(short promiseId) + { + lock (this) + { + if (!GetIsValid(promiseId)) + { + throw new ObjectDisposedException("The promise retainer was already disposed.", GetFormattedStacktrace(2)); + } + ThrowIfInPool(this); + + Retain(); + } + return GetWaitAsync(promiseId); + } + + [MethodImpl(InlineOption)] + internal Promise WaitAsyncSkipValidation() + { +#if PROMISE_DEBUG + return WaitAsync(Id); +#else + Retain(); + return GetWaitAsync(Id); +#endif + } + + [MethodImpl(InlineOption)] + private Promise GetWaitAsync(short promiseId) + { +#if PROMISE_DEBUG + // In DEBUG mode, we return a duplicate so that its usage will be validated properly. + var duplicatePromise = PromiseDuplicate.GetOrCreate(); + HookupNewPromise(promiseId, duplicatePromise); + return new Promise(duplicatePromise, duplicatePromise.Id); +#else + // In RELEASE mode, we just return this for efficiency. + return new Promise(this, promiseId); +#endif + } + } + } // class PromiseRefBase + } // class Internal +} \ No newline at end of file diff --git a/Package/Core/Promises/Internal/PromiseRetainerInternal.cs.meta b/Package/Core/Promises/Internal/PromiseRetainerInternal.cs.meta new file mode 100644 index 00000000..b0821af3 --- /dev/null +++ b/Package/Core/Promises/Internal/PromiseRetainerInternal.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 67e0086ac81d1c743ae6188101c3bbdb +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/Promises/Internal/RejectContainersInternal.cs b/Package/Core/Promises/Internal/RejectContainersInternal.cs index a96b21cd..5494cd0f 100644 --- a/Package/Core/Promises/Internal/RejectContainersInternal.cs +++ b/Package/Core/Promises/Internal/RejectContainersInternal.cs @@ -4,7 +4,6 @@ #undef PROMISE_DEBUG #endif -#pragma warning disable IDE0038 // Use pattern matching #pragma warning disable IDE0054 // Use compound assignment #pragma warning disable IDE0090 // Use 'new(...)' @@ -20,17 +19,17 @@ partial class Internal // Extension method instead of including on the interface, since old IL2CPP compiler does not support virtual generics with structs. internal static bool TryGetValue(this IRejectContainer rejectContainer, out TValue converted) { - // null check is same as typeof(TValue).IsValueType, but is actually optimized away by the JIT. This prevents the type check when TValue is a reference type. + // These checks are optimized away by the JIT. + // Null check is necessary for older runtimes to prevent the type check when TValue is a reference type. if (null != default(TValue) && typeof(TValue) == typeof(VoidResult)) { converted = default; return true; } - object value = rejectContainer.Value; - if (value is TValue) + if (rejectContainer.Value is TValue value) { - converted = (TValue) value; + converted = value; return true; } converted = default; @@ -46,9 +45,8 @@ internal abstract class RejectContainer : IRejectContainer, ITraceable CausalityTrace ITraceable.Trace { get; set; } #endif - protected object _value; - public object Value => _value; - + public abstract object Value { get; } + public abstract Exception GetValueAsException(); public abstract void ReportUnhandled(); public abstract ExceptionDispatchInfo GetExceptionDispatchInfo(); @@ -64,7 +62,6 @@ internal static IRejectContainer Create(object reason, int rejectSkipFrames, Exc reason = reason ?? new NullReferenceException(); return reason is Exception e ? RejectionContainerException.Create(e, rejectSkipFrames + 1, exceptionWithStacktrace, traceable) - // Only need to create one object pool for reference types. : (IRejectContainer) RejectionContainer.Create(reason, rejectSkipFrames + 1, exceptionWithStacktrace, traceable); } } @@ -74,6 +71,9 @@ internal static IRejectContainer Create(object reason, int rejectSkipFrames, Exc #endif internal sealed partial class RejectionContainer : RejectContainer, IRejectionToContainer, ICantHandleException { + private object _value; + public override object Value => _value; + partial void SetCreatedAndRejectedStacktrace(int rejectSkipFrames, Exception exceptionWithStacktrace, ITraceable traceable); #if PROMISE_DEBUG private StackTrace _rejectedStackTrace; @@ -91,6 +91,9 @@ partial void SetCreatedAndRejectedStacktrace(int rejectSkipFrames, Exception exc private RejectionContainer() { } + public override Exception GetValueAsException() + => ToException(); + new internal static RejectionContainer Create(object value, int rejectSkipFrames, Exception exceptionWithStacktrace, ITraceable traceable) { var container = new RejectionContainer @@ -103,9 +106,7 @@ private RejectionContainer() { } } public override void ReportUnhandled() - { - ReportUnhandledException(ToException()); - } + => ReportUnhandledException(ToException()); private UnhandledException ToException() { @@ -173,13 +174,14 @@ partial void SetCreatedAndRejectedStacktrace(int rejectSkipFrames, Exception exc } #endif + public override object Value => _capturedInfo.SourceException; + private RejectionContainerException() { } internal static RejectionContainerException Create(Exception value, int rejectSkipFrames, Exception exceptionWithStacktrace, ITraceable traceable) { var container = new RejectionContainerException { - _value = value, _capturedInfo = ExceptionDispatchInfo.Capture(value) }; SetCreatedStacktrace(container, 2); @@ -188,16 +190,14 @@ internal static RejectionContainerException Create(Exception value, int rejectSk } public override void ReportUnhandled() - { - ReportUnhandledException(ToException()); - } + => ReportUnhandledException(ToException()); private UnhandledException ToException() { #if PROMISE_DEBUG - return new UnhandledExceptionInternal(Value, "An exception was not handled." + CausalityTraceMessage, _stackTraces.ToString(), _rejectException ?? (Exception) Value); + return new UnhandledExceptionInternal(Value, "An exception was not handled." + CausalityTraceMessage, _stackTraces.ToString(), _rejectException ?? _capturedInfo.SourceException); #else - return new UnhandledExceptionInternal(Value, "An exception was not handled." + CausalityTraceMessage, null, (Exception) Value); + return new UnhandledExceptionInternal(Value, "An exception was not handled." + CausalityTraceMessage, null, _capturedInfo.SourceException); #endif } @@ -209,6 +209,9 @@ IRejectContainer IRejectionToContainer.ToContainer(ITraceable traceable) void ICantHandleException.ReportUnhandled(ITraceable traceable) => ReportUnhandledException(ToException()); + + public override Exception GetValueAsException() + => _capturedInfo.SourceException; } } } \ No newline at end of file diff --git a/Package/Core/Promises/Manager.cs b/Package/Core/Promises/Manager.cs index 9572d9d3..3313fd39 100644 --- a/Package/Core/Promises/Manager.cs +++ b/Package/Core/Promises/Manager.cs @@ -17,7 +17,7 @@ partial struct Promise public static class Manager { /// - /// Clears all currently pooled objects. Does not affect pending or preserved promises. + /// Clears all currently pooled objects. Does not affect pending or retained promises. /// public static void ClearObjectPool() { diff --git a/Package/Core/Promises/Promise.cs b/Package/Core/Promises/Promise.cs index 462b0041..4fbc98c9 100644 --- a/Package/Core/Promises/Promise.cs +++ b/Package/Core/Promises/Promise.cs @@ -6,6 +6,7 @@ #endif using System; +using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading; @@ -55,7 +56,8 @@ public bool IsValid /// must be called when you are finished with it. /// NOTE: You should not return a preserved from a public API. Use to get a that is publicly safe. /// - [MethodImpl(Internal.InlineOption)] + /// This method is obsolete. You should instead use . + [Obsolete("Prefer Promise.GetRetainer()", false), EditorBrowsable(EditorBrowsableState.Never)] public Promise Preserve() { ValidateOperation(1); @@ -148,7 +150,8 @@ public bool TryWaitNoThrow(TimeSpan timeout, out ResultContainer resultContainer /// Preserved promises are unsafe to return from public APIs. Use to get a that is publicly safe. /// is safe to call even if you are unsure if this is preserved. /// - [MethodImpl(Internal.InlineOption)] + /// This method is obsolete. You should instead use with . + [Obsolete("Prefer Promise.Retainer.WaitAsync()", false), EditorBrowsable(EditorBrowsableState.Never)] public Promise Duplicate() { ValidateOperation(1); diff --git a/Package/Core/Promises/PromiseStatic.cs b/Package/Core/Promises/PromiseStatic.cs index e1d7d427..1f7e5813 100644 --- a/Package/Core/Promises/PromiseStatic.cs +++ b/Package/Core/Promises/PromiseStatic.cs @@ -147,7 +147,7 @@ public static PromiseSwitchToContextAwaiter SwitchToForegroundAwait(bool forceAs /// /// If true, forces the context switch to happen asynchronously. /// - /// This method is equivalent to , butbut is more efficient when used directly with the keyword. + /// This method is equivalent to , but is more efficient when used directly with the keyword. /// public static PromiseSwitchToContextAwaiter SwitchToBackgroundAwait(bool forceAsync = false) => new PromiseSwitchToContextAwaiter(Config.BackgroundContext, forceAsync); diff --git a/Package/Core/Promises/PromiseStaticEach.cs b/Package/Core/Promises/PromiseStaticEach.cs new file mode 100644 index 00000000..1278f9af --- /dev/null +++ b/Package/Core/Promises/PromiseStaticEach.cs @@ -0,0 +1,250 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using Proto.Promises.Linq; +using System; +using System.Collections.Generic; + +namespace Proto.Promises +{ + public partial struct Promise + { + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + return Each(Internal.GetEnumerator(promise1, promise2)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2, Promise promise3) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + ValidateArgument(promise3, nameof(promise3), 1); + return Each(Internal.GetEnumerator(promise1, promise2, promise3)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2, Promise promise3, Promise promise4) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + ValidateArgument(promise3, nameof(promise3), 1); + ValidateArgument(promise4, nameof(promise4), 1); + return Each(Internal.GetEnumerator(promise1, promise2, promise3, promise4)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(params Promise[] promises) + { + ValidateArgument(promises, nameof(promises), 1); + return Each(promises.GetGenericEnumerator()); + } + + // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, + // so we only include this in the nuget package and netstandard2.1+. +#if !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(ReadOnlySpan promises) + => Each(promises.GetPersistedEnumerator()); +#endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(IEnumerable promises) + { + ValidateArgument(promises, nameof(promises), 1); + return Each(promises.GetEnumerator()); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(TEnumerator promises) where TEnumerator : IEnumerator + { + ValidateArgument(promises, nameof(promises), 1); + + using (promises) + { + if (!promises.MoveNext()) + { + return AsyncEnumerable.Empty(); + } + + var eachPromise = Internal.GetOrCreatePromiseEachAsyncEnumerableVoid(); + do + { + var promise = promises.Current; + ValidateElement(promise, "promises", 1); + if (promise._ref == null) + { + eachPromise.AddResult(ResultContainer.Resolved); + } + else + { + eachPromise.AddPromise(promise._ref, promise._id); + } + } while (promises.MoveNext()); + return new AsyncEnumerable(eachPromise); + } + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(Promise promise1, Promise promise2) + => Promise.Each(promise1, promise2); + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(Promise promise1, Promise promise2, Promise promise3) + => Promise.Each(promise1, promise2, promise3); + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(Promise promise1, Promise promise2, Promise promise3, Promise promise4) + => Promise.Each(promise1, promise2, promise3, promise4); + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(params Promise[] promises) + => Promise.Each(promises); + + // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, + // so we only include this in the nuget package and netstandard2.1+. +#if !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(ReadOnlySpan> promises) + => Promise.Each(promises.GetPersistedEnumerator()); +#endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(IEnumerable> promises) + => Promise.Each(promises); + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable.ResultContainer> Each(TEnumerator promises) where TEnumerator : IEnumerator> + => Promise.Each(promises); + } + + public partial struct Promise + { + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + return Each(Internal.GetEnumerator(promise1, promise2)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2, Promise promise3) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + ValidateArgument(promise3, nameof(promise3), 1); + return Each(Internal.GetEnumerator(promise1, promise2, promise3)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(Promise promise1, Promise promise2, Promise promise3, Promise promise4) + { + ValidateArgument(promise1, nameof(promise1), 1); + ValidateArgument(promise2, nameof(promise2), 1); + ValidateArgument(promise3, nameof(promise3), 1); + ValidateArgument(promise4, nameof(promise4), 1); + return Each(Internal.GetEnumerator(promise1, promise2, promise3, promise4)); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(params Promise[] promises) + { + ValidateArgument(promises, nameof(promises), 1); + return Each(promises.GetGenericEnumerator()); + } + + // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, + // so we only include this in the nuget package and netstandard2.1+. +#if !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(ReadOnlySpan> promises) + => Each(promises.GetPersistedEnumerator()); +#endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(IEnumerable> promises) + { + ValidateArgument(promises, nameof(promises), 1); + return Each(promises.GetEnumerator()); + } + + /// + /// Creates an that will yield the results of the supplied promises as those promises complete. + /// + public static AsyncEnumerable Each(TEnumerator promises) where TEnumerator : IEnumerator> + { + ValidateArgument(promises, nameof(promises), 1); + + using (promises) + { + if (!promises.MoveNext()) + { + return AsyncEnumerable.Empty(); + } + + var eachPromise = Internal.GetOrCreatePromiseEachAsyncEnumerable(); + do + { + var promise = promises.Current; + ValidateElement(promise, "promises", 1); + if (promise._ref == null) + { + eachPromise.AddResult(promise._result); + } + else + { + eachPromise.AddPromise(promise._ref, promise._id); + } + } while (promises.MoveNext()); + return new AsyncEnumerable(eachPromise); + } + } + } +} \ No newline at end of file diff --git a/Package/Core/Promises/PromiseStaticEach.cs.meta b/Package/Core/Promises/PromiseStaticEach.cs.meta new file mode 100644 index 00000000..0253354d --- /dev/null +++ b/Package/Core/Promises/PromiseStaticEach.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: a1792625fd5611141ae7d78db68bbeb5 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/Promises/PromiseStaticMerge.cs b/Package/Core/Promises/PromiseStaticMerge.cs index 855f07a3..4fd4b2da 100644 --- a/Package/Core/Promises/PromiseStaticMerge.cs +++ b/Package/Core/Promises/PromiseStaticMerge.cs @@ -54,7 +54,10 @@ public static Promise All(Promise promise1, Promise promise2, Promise promise3, /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise All(params Promise[] promises) - => All(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return All(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -72,7 +75,10 @@ public static Promise All(ReadOnlySpan promises) /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise All(IEnumerable promises) - => All(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return All(promises.GetEnumerator()); + } /// /// Returns a that will resolve when all have resolved. @@ -188,7 +194,7 @@ public static Promise> All(TEnumerator promises, IList< #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif - private static partial class MergeResultFuncs + internal static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -233,7 +239,9 @@ public static Promise Merge(Promise promise1, Promise promise2) ValidateArgument(promise2, nameof(promise2), 1); if (promise2._ref == null) { +#pragma warning disable CS0618 // Type or member is obsolete return promise1.Duplicate(); +#pragma warning restore CS0618 // Type or member is obsolete } uint pendingCount = 1; @@ -252,7 +260,7 @@ public static Promise Merge(Promise promise1, Promise promise2) return new Promise(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -348,7 +356,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, int index, r return new Promise<(T1, T2)>(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -453,7 +461,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, int index, r return new Promise<(T1, T2, T3)>(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -567,7 +575,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, int index, r return new Promise<(T1, T2, T3, T4)>(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -690,7 +698,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, int index, r return new Promise<(T1, T2, T3, T4, T5)>(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -822,7 +830,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, int index, r return new Promise<(T1, T2, T3, T4, T5, T6)>(promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] diff --git a/Package/Core/Promises/PromiseStaticMergeSettled.cs b/Package/Core/Promises/PromiseStaticMergeSettled.cs index 2d1908f1..7aa2685b 100644 --- a/Package/Core/Promises/PromiseStaticMergeSettled.cs +++ b/Package/Core/Promises/PromiseStaticMergeSettled.cs @@ -16,17 +16,17 @@ namespace Proto.Promises public partial struct Promise { [MethodImpl(Internal.InlineOption)] - private static void GetAllResultContainer(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref IList result) - => result[index] = new ResultContainer(rejectContainer, state); + private static void GetAllResultContainer(Internal.PromiseRefBase handler, int index, ref IList result) + => result[index] = new ResultContainer(handler._rejectContainer, handler.State); #if NETCOREAPP || UNITY_2021_2_OR_NEWER - private static unsafe Internal.GetResultContainerDelegate> GetAllResultContainerFunc + private static unsafe Internal.GetResultDelegate> GetAllResultContainerFunc { [MethodImpl(Internal.InlineOption)] get => new(&GetAllResultContainer); } #else - private static readonly Internal.GetResultContainerDelegate> GetAllResultContainerFunc = GetAllResultContainer; + private static readonly Internal.GetResultDelegate> GetAllResultContainerFunc = GetAllResultContainer; #endif /// @@ -39,7 +39,7 @@ public static Promise> AllSettled(Promise promise1, Promi { ValidateArgument(promise1, nameof(promise1), 1); ValidateArgument(promise2, nameof(promise2), 1); - return AllSettled(Internal.GetEnumerator(promise1, promise2)); + return AllSettled(Internal.GetEnumerator(promise1, promise2), valueContainer); } /// @@ -54,7 +54,7 @@ public static Promise> AllSettled(Promise promise1, Promi ValidateArgument(promise1, nameof(promise1), 1); ValidateArgument(promise2, nameof(promise2), 1); ValidateArgument(promise3, nameof(promise3), 1); - return AllSettled(Internal.GetEnumerator(promise1, promise2, promise3)); + return AllSettled(Internal.GetEnumerator(promise1, promise2, promise3), valueContainer); } /// @@ -71,12 +71,13 @@ public static Promise> AllSettled(Promise promise1, Promi ValidateArgument(promise2, nameof(promise2), 1); ValidateArgument(promise3, nameof(promise3), 1); ValidateArgument(promise4, nameof(promise4), 1); - return AllSettled(Internal.GetEnumerator(promise1, promise2, promise3, promise4)); + return AllSettled(Internal.GetEnumerator(promise1, promise2, promise3, promise4), valueContainer); } /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// + /// The promises to combine. public static Promise> AllSettled(params Promise[] promises) => AllSettled(promises, new ResultContainer[promises.Length]); @@ -86,8 +87,10 @@ public static Promise> AllSettled(params Promise[] promis /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// - public static Promise> AllSettled(ReadOnlySpan promises) - => AllSettled(promises.GetPersistedEnumerator()); + /// The promises to combine. + /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. + public static Promise> AllSettled(ReadOnlySpan promises, IList valueContainer = null) + => AllSettled(promises.GetPersistedEnumerator(), valueContainer); #endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER /// @@ -96,7 +99,10 @@ public static Promise> AllSettled(ReadOnlySpan p /// The promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. public static Promise> AllSettled(Promise[] promises, IList valueContainer = null) - => AllSettled(promises.GetGenericEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return AllSettled(promises.GetGenericEnumerator(), valueContainer); + } /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. @@ -104,7 +110,10 @@ public static Promise> AllSettled(Promise[] promises, ILi /// The promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. public static Promise> AllSettled(IEnumerable promises, IList valueContainer = null) - => AllSettled(promises.GetEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return AllSettled(promises.GetEnumerator(), valueContainer); + } /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. @@ -229,6 +238,7 @@ public static Promise> AllSettled(TEnumerato /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// + /// The promises to combine. public static Promise.ResultContainer>> AllSettled(params Promise[] promises) => Promise.AllSettled(promises); @@ -238,8 +248,10 @@ public static Promise> AllSettled(TEnumerato /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// - public static Promise.ResultContainer>> AllSettled(ReadOnlySpan> promises) - => Promise.AllSettled(promises.GetPersistedEnumerator()); + /// The promises to combine. + /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. + public static Promise.ResultContainer>> AllSettled(ReadOnlySpan> promises, IList.ResultContainer> valueContainer = null) + => Promise.AllSettled(promises.GetPersistedEnumerator(), valueContainer); #endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER /// @@ -247,20 +259,20 @@ public static Promise> AllSettled(TEnumerato /// /// The promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. - public static Promise.ResultContainer>> AllSettled(IEnumerable> promises, IList valueContainer = null) - => Promise.AllSettled(promises); + public static Promise.ResultContainer>> AllSettled(IEnumerable> promises, IList.ResultContainer> valueContainer = null) + => Promise.AllSettled(promises, valueContainer); /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// /// The enumerator of promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. - public static Promise.ResultContainer>> AllSettled(TEnumerator promises, IList valueContainer = null) where TEnumerator : IEnumerator> - => Promise.AllSettled(promises); + public static Promise.ResultContainer>> AllSettled(TEnumerator promises, IList.ResultContainer> valueContainer = null) where TEnumerator : IEnumerator> + => Promise.AllSettled(promises, valueContainer); #region 2Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -268,39 +280,39 @@ private static partial class MergeResultFuncs private static class Settled2 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled(Promise promise1, Promise promise2) + public static Promise<(ResultContainer, ResultContainer)> MergeSettled(Promise promise1, Promise promise2) { ValidateArgument(promise1, nameof(promise1), 1); ValidateArgument(promise2, nameof(promise2), 1); @@ -329,7 +341,7 @@ public static Promise> MergeSettled promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -337,39 +349,39 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer>> MergeSettled(Promise promise1, Promise promise2) + public static Promise<(Promise.ResultContainer, ResultContainer)> MergeSettled(Promise promise1, Promise promise2) { ValidateArgument(promise1, nameof(promise1), 1); ValidateArgument(promise2, nameof(promise2), 1); @@ -398,7 +410,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -406,32 +418,32 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } @@ -439,7 +451,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe /// Returns a of that will resolve with the values of the promises when they have all resolved. /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// - public static Promise.ResultContainer, Promise.ResultContainer>> MergeSettled(Promise promise1, Promise promise2) + public static Promise<(Promise.ResultContainer, Promise.ResultContainer)> MergeSettled(Promise promise1, Promise promise2) { ValidateArgument(promise1, nameof(promise1), 1); ValidateArgument(promise2, nameof(promise2), 1); @@ -472,7 +484,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe #region 3Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -480,42 +492,42 @@ private static partial class MergeResultFuncs private static class Settled3 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer)> GetSettled3() => Settled3.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled( + public static Promise<(ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3) { ValidateArgument(promise1, nameof(promise1), 1); @@ -548,7 +560,7 @@ public static Promise { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3) { ValidateArgument(promise1, nameof(promise1), 1); @@ -624,7 +636,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -632,42 +644,42 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3) { ValidateArgument(promise1, nameof(promise1), 1); @@ -700,7 +712,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -708,42 +720,42 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3) { ValidateArgument(promise1, nameof(promise1), 1); @@ -780,7 +792,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe #region 4Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -788,45 +800,45 @@ private static partial class MergeResultFuncs private static class Settled4 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled4() => Settled4.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled( + public static Promise<(ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4) { ValidateArgument(promise1, nameof(promise1), 1); @@ -862,7 +874,7 @@ public static Promise { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled3() => Settled3.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4) { ValidateArgument(promise1, nameof(promise1), 1); @@ -944,7 +956,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -952,45 +964,45 @@ private static partial class MergeResultFuncs private static class Settled2 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1026,7 +1038,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1034,46 +1046,46 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1109,7 +1121,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1117,46 +1129,46 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1196,7 +1208,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe #region 5Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1204,48 +1216,48 @@ private static partial class MergeResultFuncs private static class Settled5 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled5() => Settled5.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled( + public static Promise<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1284,7 +1296,7 @@ public static Promise { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled4() => Settled4.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1372,7 +1384,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1380,48 +1392,48 @@ private static partial class MergeResultFuncs private static class Settled3 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func = GetMergeResult; + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled3() => Settled3.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1460,7 +1472,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1468,49 +1480,49 @@ private static partial class MergeResultFuncs private static class Settled2 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1549,7 +1561,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1557,49 +1569,49 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1638,7 +1650,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1646,49 +1658,49 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1731,7 +1743,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe #region 6Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1739,52 +1751,52 @@ private static partial class MergeResultFuncs private static class Settled6 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled6() => Settled6.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled( + public static Promise<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1826,7 +1838,7 @@ public static Promise { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled5() => Settled5.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -1921,7 +1933,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -1929,52 +1941,52 @@ private static partial class MergeResultFuncs private static class Settled4 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled4() => Settled4.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2016,7 +2028,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2024,52 +2036,52 @@ private static partial class MergeResultFuncs private static class Settled3 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled3() => Settled3.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2111,7 +2123,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2119,52 +2131,52 @@ private static partial class MergeResultFuncs private static class Settled2 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2206,7 +2218,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2214,52 +2226,52 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2301,7 +2313,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2309,52 +2321,52 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item6 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2400,7 +2412,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe #region 7Args - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2408,55 +2420,55 @@ private static partial class MergeResultFuncs private static class Settled7 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new ResultContainer(rejectContainer, state); + result.Item1 = new ResultContainer(handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate> Func + internal static unsafe Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate> Func + internal static readonly Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate> + internal static Internal.GetResultDelegate<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled7() => Settled7.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise> MergeSettled( + public static Promise<(ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2501,7 +2513,7 @@ public static Promise { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new ResultContainer(rejectContainer, state); + result.Item2 = new ResultContainer(handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled6() => Settled6.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2602,7 +2614,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2610,55 +2622,55 @@ private static partial class MergeResultFuncs private static class Settled5 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new ResultContainer(rejectContainer, state); + result.Item3 = new ResultContainer(handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled5() => Settled5.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2703,7 +2715,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2711,55 +2723,55 @@ private static partial class MergeResultFuncs private static class Settled4 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new ResultContainer(rejectContainer, state); + result.Item4 = new ResultContainer(handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled4() => Settled4.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2804,7 +2816,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2812,55 +2824,55 @@ private static partial class MergeResultFuncs private static class Settled3 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new ResultContainer(rejectContainer, state); + result.Item5 = new ResultContainer(handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> GetSettled3() => Settled3.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -2905,7 +2917,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -2913,55 +2925,55 @@ private static partial class MergeResultFuncs private static class Settled2 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new ResultContainer(rejectContainer, state); + result.Item6 = new ResultContainer(handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> GetSettled2() => Settled2.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -3006,7 +3018,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -3014,55 +3026,55 @@ private static partial class MergeResultFuncs private static class Settled1 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item6 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new ResultContainer(rejectContainer, state); + result.Item7 = new ResultContainer(handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> GetSettled1() => Settled1.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); @@ -3107,7 +3119,7 @@ private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRe promise, promise.Id); } - private static partial class MergeResultFuncs + static partial class MergeResultFuncs { #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] @@ -3115,55 +3127,55 @@ private static partial class MergeResultFuncs private static class Settled0 { [MethodImpl(Internal.InlineOption)] - private static void GetMergeResult(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, State state, int index, ref ValueTuple.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer> result) + private static void GetMergeResult(Internal.PromiseRefBase handler, int index, ref (Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer) result) { switch (index) { case 0: - result.Item1 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item1 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 1: - result.Item2 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item2 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 2: - result.Item3 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item3 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 3: - result.Item4 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item4 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 4: - result.Item5 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item5 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 5: - result.Item6 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item6 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; case 6: - result.Item7 = new Promise.ResultContainer(handler.GetResult(), rejectContainer, state); + result.Item7 = new Promise.ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); break; } } #if NETCOREAPP || UNITY_2021_2_OR_NEWER - internal static unsafe Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static unsafe Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func { [MethodImpl(Internal.InlineOption)] get => new(&GetMergeResult); } #else - internal static readonly Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> Func + internal static readonly Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> Func = GetMergeResult; #endif } [MethodImpl(Internal.InlineOption)] - internal static Internal.GetResultContainerDelegate.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> + internal static Internal.GetResultDelegate<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> GetSettled0() => Settled0.Func; } /// /// Returns a that will resolve with the result container of each promise when they have all completed. /// - public static Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer>> MergeSettled( + public static Promise<(Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer, Promise.ResultContainer)> MergeSettled( Promise promise1, Promise promise2, Promise promise3, Promise promise4, Promise promise5, Promise promise6, Promise promise7) { ValidateArgument(promise1, nameof(promise1), 1); diff --git a/Package/Core/Promises/PromiseStaticRace.cs b/Package/Core/Promises/PromiseStaticRace.cs index c16659da..fb06ed0b 100644 --- a/Package/Core/Promises/PromiseStaticRace.cs +++ b/Package/Core/Promises/PromiseStaticRace.cs @@ -52,7 +52,10 @@ public static Promise Race(Promise promise1, Promise promise2, Promise promise3, /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise Race(params Promise[] promises) - => Race(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return Race(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -70,7 +73,10 @@ public static Promise Race(ReadOnlySpan promises) /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise Race(IEnumerable promises) - => Race(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return Race(promises.GetEnumerator()); + } /// /// Returns a that will resolve when the first of the has resolved. @@ -167,7 +173,10 @@ public static Promise RaceWithIndex(Promise promise1, Promise promise2, Pro /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise RaceWithIndex(params Promise[] promises) - => RaceWithIndex(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -185,7 +194,10 @@ public static Promise RaceWithIndex(ReadOnlySpan promises) /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise RaceWithIndex(IEnumerable promises) - => RaceWithIndex(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index of that promise. @@ -339,7 +351,10 @@ public static Promise First(Promise promise1, Promise promise2, Promise promise3 /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise First(params Promise[] promises) - => First(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return First(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -357,7 +372,10 @@ public static Promise First(ReadOnlySpan promises) /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise First(IEnumerable promises) - => First(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return First(promises.GetEnumerator()); + } /// /// Returns a that will resolve when the first of the has resolved. @@ -457,7 +475,10 @@ public static Promise FirstWithIndex(Promise promise1, Promise promise2, Pr /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise FirstWithIndex(params Promise[] promises) - => FirstWithIndex(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -475,7 +496,10 @@ public static Promise FirstWithIndex(ReadOnlySpan promises) /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise FirstWithIndex(IEnumerable promises) - => FirstWithIndex(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index of that promise. @@ -633,7 +657,10 @@ public static Promise First(TEnumerator promises) where TEnum /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise<(int winIndex, T result)> RaceWithIndex(params Promise[] promises) - => RaceWithIndex>>(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex>>(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -651,7 +678,10 @@ public static Promise First(TEnumerator promises) where TEnum /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise<(int winIndex, T result)> RaceWithIndex(IEnumerable> promises) - => RaceWithIndex>>(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex>>(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index and result of that promise. @@ -756,7 +786,10 @@ public static Promise First(TEnumerator promises) where TEnum /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise<(int winIndex, T result)> FirstWithIndex(params Promise[] promises) - => FirstWithIndex>>(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex>>(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -774,7 +807,10 @@ public static Promise First(TEnumerator promises) where TEnum /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise<(int winIndex, T result)> FirstWithIndex(IEnumerable> promises) - => FirstWithIndex>>(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex>>(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index and result of that promise. diff --git a/Package/Core/Promises/PromiseT.cs b/Package/Core/Promises/PromiseT.cs index c73caa35..9867a80d 100644 --- a/Package/Core/Promises/PromiseT.cs +++ b/Package/Core/Promises/PromiseT.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.CompilerServices; using System.Threading; @@ -89,6 +90,8 @@ public static implicit operator Promise(in Promise rhs) /// must be called when you are finished with it. /// NOTE: You should not return a preserved from a public API. Use to get a that is publicly safe. /// + /// This method is obsolete. You should instead use . + [Obsolete("Prefer Promise.GetRetainer()", false), EditorBrowsable(EditorBrowsableState.Never)] public Promise Preserve() { ValidateOperation(1); @@ -186,6 +189,8 @@ public bool TryWaitForResultNoThrow(TimeSpan timeout, out ResultContainer result /// Preserved promises are unsafe to return from public APIs. Use to get a that is publicly safe. /// is safe to call even if you are unsure if this is preserved. /// + /// This method is obsolete. You should instead use with . + [Obsolete("Prefer Promise.Retainer.WaitAsync()", false), EditorBrowsable(EditorBrowsableState.Never)] public Promise Duplicate() { ValidateOperation(1); diff --git a/Package/Core/Promises/PromiseTStatic.cs b/Package/Core/Promises/PromiseTStatic.cs index b45fda8e..671633e2 100644 --- a/Package/Core/Promises/PromiseTStatic.cs +++ b/Package/Core/Promises/PromiseTStatic.cs @@ -56,7 +56,10 @@ public static Promise Race(Promise promise1, Promise promise2, Promise< /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise Race(params Promise[] promises) - => Race(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return Race(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -74,7 +77,10 @@ public static Promise Race(ReadOnlySpan> promises) /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise Race(IEnumerable> promises) - => Race(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return Race(promises.GetEnumerator()); + } /// /// Returns a that will resolve when the first of the has resolved with the same value as that promise. @@ -181,7 +187,10 @@ public static Promise First(Promise promise1, Promise promise2, Promise /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise First(params Promise[] promises) - => First(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return First(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -199,7 +208,10 @@ public static Promise First(ReadOnlySpan> promises) /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise First(IEnumerable> promises) - => First(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return First(promises.GetEnumerator()); + } /// /// Returns a that will resolve when the first of the has resolved with the same value as that promise. @@ -299,7 +311,10 @@ public static Promise First(TEnumerator promises) where TEnumera /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise<(int winIndex, T result)> RaceWithIndex(params Promise[] promises) - => RaceWithIndex(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -317,13 +332,16 @@ public static Promise First(TEnumerator promises) where TEnumera /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// public static Promise<(int winIndex, T result)> RaceWithIndex(IEnumerable> promises) - => RaceWithIndex(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return RaceWithIndex(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index and result of that promise. /// If any promise is rejected or canceled, the returned will immediately be canceled or rejected with the same reason. /// - public static Promise> RaceWithIndex(TEnumerator promises) where TEnumerator : IEnumerator> + public static Promise<(int winIndex, T result)> RaceWithIndex(TEnumerator promises) where TEnumerator : IEnumerator> => Promise.RaceWithIndex(promises); /// @@ -352,7 +370,10 @@ public static Promise> RaceWithIndex(TEnumerator /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise<(int winIndex, T result)> FirstWithIndex(params Promise[] promises) - => FirstWithIndex(promises.GetGenericEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex(promises.GetGenericEnumerator()); + } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, // so we only include this in the nuget package and netstandard2.1+. @@ -370,13 +391,16 @@ public static Promise> RaceWithIndex(TEnumerator /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// public static Promise<(int winIndex, T result)> FirstWithIndex(IEnumerable> promises) - => FirstWithIndex(promises.GetEnumerator()); + { + ValidateArgument(promises, nameof(promises), 1); + return FirstWithIndex(promises.GetEnumerator()); + } /// /// Returns a of that will resolve when the first of the promises has resolved with the index and result of that promise. /// If all promises are rejected or canceled, the returned will be canceled or rejected with the same reason as the last that is rejected or canceled. /// - public static Promise> FirstWithIndex(TEnumerator promises) where TEnumerator : IEnumerator> + public static Promise<(int winIndex, T result)> FirstWithIndex(TEnumerator promises) where TEnumerator : IEnumerator> => Promise.FirstWithIndex(promises); #endif // UNITY_2021_2_OR_NEWER || NETSTANDARD2_1_OR_GREATER || NETCOREAPP @@ -470,7 +494,10 @@ public static Promise> All(ReadOnlySpan> promises, IList /// The promises to combine. /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. public static Promise> All(Promise[] promises, IList valueContainer = null) - => All(promises.GetGenericEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return All(promises.GetGenericEnumerator(), valueContainer); + } /// /// Returns a that will resolve with a list of values in the same order as s when they have all resolved. @@ -479,7 +506,10 @@ public static Promise> All(Promise[] promises, IList valueContain /// The promises to combine. /// Optional list that will be used to contain the resolved values. If it is not provided, a new one will be created. public static Promise> All(IEnumerable> promises, IList valueContainer = null) - => All(promises.GetEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return All(promises.GetEnumerator(), valueContainer); + } /// /// Returns a that will resolve a list of values in the same order as when they have all resolved. @@ -573,17 +603,17 @@ public static Promise> All(TEnumerator promises, IList } [MethodImpl(Internal.InlineOption)] - private static void GetAllResultContainer(Internal.PromiseRefBase handler, Internal.IRejectContainer rejectContainer, Promise.State state, int index, ref IList result) - => result[index] = new ResultContainer(handler.GetResult(), rejectContainer, state); + private static void GetAllResultContainer(Internal.PromiseRefBase handler, int index, ref IList result) + => result[index] = new ResultContainer(handler.GetResult(), handler._rejectContainer, handler.State); #if NETCOREAPP || UNITY_2021_2_OR_NEWER - private static unsafe Internal.GetResultContainerDelegate> GetAllResultContainerFunc + private static unsafe Internal.GetResultDelegate> GetAllResultContainerFunc { [MethodImpl(Internal.InlineOption)] get => new(&GetAllResultContainer); } #else - private static readonly Internal.GetResultContainerDelegate> GetAllResultContainerFunc = GetAllResultContainer; + private static readonly Internal.GetResultDelegate> GetAllResultContainerFunc = GetAllResultContainer; #endif /// @@ -634,6 +664,7 @@ public static Promise> AllSettled(Promise promise1, Pr /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. /// + /// The promises to combine. public static Promise> AllSettled(params Promise[] promises) => AllSettled(promises, new ResultContainer[promises.Length]); @@ -655,7 +686,10 @@ public static Promise> AllSettled(ReadOnlySpan /// The promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. public static Promise> AllSettled(Promise[] promises, IList valueContainer = null) - => AllSettled(promises.GetGenericEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return AllSettled(promises.GetGenericEnumerator(), valueContainer); + } /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. @@ -663,7 +697,10 @@ public static Promise> AllSettled(Promise[] promises, /// The promises to combine. /// Optional list that will be used to contain the result containers. If it is not provided, a new one will be created. public static Promise> AllSettled(IEnumerable> promises, IList valueContainer = null) - => AllSettled(promises.GetEnumerator(), valueContainer); + { + ValidateArgument(promises, nameof(promises), 1); + return AllSettled(promises.GetEnumerator(), valueContainer); + } /// /// Returns a that will resolve with a list of s in the same order as when they have all completed. diff --git a/Package/Core/Promises/ResultContainers.cs b/Package/Core/Promises/ResultContainers.cs index 27e39a51..dc66c4c4 100644 --- a/Package/Core/Promises/ResultContainers.cs +++ b/Package/Core/Promises/ResultContainers.cs @@ -11,6 +11,14 @@ namespace Proto.Promises { + partial class Internal + { + internal interface IResultContainer + { + IRejectContainer RejectContainer { get; } + } + } + partial struct Promise { /// @@ -19,7 +27,7 @@ partial struct Promise #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif - public readonly struct ResultContainer + public readonly struct ResultContainer : Internal.IResultContainer { internal static ResultContainer Resolved { @@ -29,6 +37,12 @@ internal static ResultContainer Resolved internal readonly Promise.ResultContainer _target; + Internal.IRejectContainer Internal.IResultContainer.RejectContainer + { + [MethodImpl(Internal.InlineOption)] + get => _target._rejectContainer; + } + [MethodImpl(Internal.InlineOption)] internal ResultContainer(Internal.IRejectContainer rejectContainer, State state) { @@ -90,12 +104,18 @@ partial struct Promise #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif - public readonly partial struct ResultContainer + public readonly struct ResultContainer : Internal.IResultContainer { internal readonly Internal.IRejectContainer _rejectContainer; private readonly Promise.State _state; private readonly T _result; + Internal.IRejectContainer Internal.IResultContainer.RejectContainer + { + [MethodImpl(Internal.InlineOption)] + get => _rejectContainer; + } + [MethodImpl(Internal.InlineOption)] internal ResultContainer(in T result, Internal.IRejectContainer rejectContainer, Promise.State state) { @@ -104,12 +124,6 @@ internal ResultContainer(in T result, Internal.IRejectContainer rejectContainer, _result = result; } - [MethodImpl(Internal.InlineOption)] - private ResultContainer(Internal.IRejectContainer rejectContainer, Promise.State state) - : this(default, rejectContainer, state) - { - } - /// /// If the is rejected or canceled, rethrow the reason. /// @@ -175,10 +189,7 @@ public T Value /// [MethodImpl(Internal.InlineOption)] public static implicit operator Promise.ResultContainer(ResultContainer rhs) - { - var newContainer = new Promise.ResultContainer(rhs._rejectContainer, rhs._state); - return new Promise.ResultContainer(newContainer); - } + => new Promise.ResultContainer(rhs._rejectContainer, rhs._state); /// /// Wrap the value in . diff --git a/Package/Core/Promises/Retainers.cs b/Package/Core/Promises/Retainers.cs new file mode 100644 index 00000000..56f3764d --- /dev/null +++ b/Package/Core/Promises/Retainers.cs @@ -0,0 +1,159 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +# endif + +using Proto.Promises.CompilerServices; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace Proto.Promises +{ + partial struct Promise + { + /// + /// Retains this so that it can be awaited multiple times. + /// + /// A that contains the retained . + public Retainer GetRetainer() + { + ValidateOperation(1); + var promiseRef = _ref; + if (promiseRef == null) + { + return default; + } +#if !PROMISE_DEBUG + // In RELEASE mode, we can return a default retainer if this is already resolved. + // In DEBUG mode, we always want to create a backing reference for validation purposes. + if (promiseRef.State == State.Resolved) + { + promiseRef.MaybeMarkAwaitedAndDispose(_id); + return default; + } +#endif + return new Retainer(Internal.PromiseRefBase.PromiseRetainer.GetOrCreateAndHookup(promiseRef, _id)); + } + + /// + /// Retains a so that it may be awaited multiple times. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct Retainer : IDisposable + { + private readonly Promise.Retainer _impl; + + [MethodImpl(Internal.InlineOption)] + internal Retainer(Internal.PromiseRefBase.PromiseRetainer retainerRef) + { + _impl = new Promise.Retainer(retainerRef, retainerRef.Id); + } + + /// + /// Returns a that adopts the state of the retained . + /// + public Promise WaitAsync() + => _impl.WaitAsync(); + + /// + /// Asynchronous infrastructure support. This method permits instances of to be awaited. + /// + [MethodImpl(Internal.InlineOption), EditorBrowsable(EditorBrowsableState.Never)] + public PromiseAwaiterVoid GetAwaiter() + => WaitAsync().GetAwaiter(); + + /// + /// Releases the retained promise. + /// + public void Dispose() + => _impl.Dispose(); + } + } + + partial struct Promise + { + /// + /// Retains this so that it can be awaited multiple times. + /// + /// A that contains the retained . + public Retainer GetRetainer() + { + ValidateOperation(1); + var promiseRef = _ref; + if (promiseRef == null) + { + return new Retainer(_result); + } +#if !PROMISE_DEBUG + // In RELEASE mode, we can return a retainer with only the result if this is already resolved. + // In DEBUG mode, we always want to create a backing reference for validation purposes. + if (promiseRef.State == Promise.State.Resolved) + { + var retainer = new Retainer(promiseRef._result); + promiseRef.MaybeMarkAwaitedAndDispose(_id); + return retainer; + } +#endif + var retainerRef = Internal.PromiseRefBase.PromiseRetainer.GetOrCreateAndHookup(promiseRef, _id); + return new Retainer(retainerRef, retainerRef.Id); + } + + /// + /// Retains a so that it may be awaited multiple times. + /// +#if !PROTO_PROMISE_DEVELOPER_MODE + [DebuggerNonUserCode, StackTraceHidden] +#endif + public readonly struct Retainer : IDisposable + { + private readonly Internal.PromiseRefBase.PromiseRetainer _ref; + private readonly T _result; + private readonly short _id; + + [MethodImpl(Internal.InlineOption)] + internal Retainer(Internal.PromiseRefBase.PromiseRetainer retainerRef, short retainerId) + { + _ref = retainerRef; + _result = default; + _id = retainerId; + } + + [MethodImpl(Internal.InlineOption)] + internal Retainer(in T result) + { + _ref = default; + _result = result; + _id = 0; + } + + /// + /// Returns a that adopts the state of the retained . + /// + public Promise WaitAsync() + { + var promiseRef = _ref; + return promiseRef == null + ? Promise.Resolved(_result) + : promiseRef.WaitAsync(_id); + } + + /// + /// Asynchronous infrastructure support. This method permits instances of to be awaited. + /// + [MethodImpl(Internal.InlineOption), EditorBrowsable(EditorBrowsableState.Never)] + public PromiseAwaiter GetAwaiter() + => WaitAsync().GetAwaiter(); + + /// + /// Releases the retained promise. + /// + public void Dispose() + => _ref?.Dispose(_id); + } + } +} \ No newline at end of file diff --git a/Package/Core/Promises/Retainers.cs.meta b/Package/Core/Promises/Retainers.cs.meta new file mode 100644 index 00000000..0ac82938 --- /dev/null +++ b/Package/Core/Promises/Retainers.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 626aca6f91192574492ff31634e80720 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Core/Threading/PromiseSynchronizationContext.cs b/Package/Core/Threading/PromiseSynchronizationContext.cs index ef719f04..0dd5e1f2 100644 --- a/Package/Core/Threading/PromiseSynchronizationContext.cs +++ b/Package/Core/Threading/PromiseSynchronizationContext.cs @@ -51,8 +51,14 @@ internal void Invoke() #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif - private sealed class SendCallback : Internal.HandleablePromiseBase + private sealed class SendCallback : Internal.HandleablePromiseBase, Internal.ILinked { + SendCallback Internal.ILinked.Next + { + get => _next.UnsafeAs(); + set => _next = value; + } + private PostCallback _callback; private ExceptionDispatchInfo _capturedInfo; private readonly AutoResetEvent _callbackCompletedEvent = new AutoResetEvent(false); @@ -118,7 +124,7 @@ private void Dispose() private PostCallback[] _postQueue; private PostCallback[] _executing; // These must not be readonly. - private Internal.ValueLinkedQueue _sendQueue = new Internal.ValueLinkedQueue(); + private Internal.ValueLinkedQueue _sendQueue = new Internal.ValueLinkedQueue(); private Internal.SpinLocker _syncLocker; private int _postCount; private bool _isInvoking; @@ -228,22 +234,23 @@ public void Execute() // We don't need to catch exceptions here because it's already handled in the SendCallback.Invoke(). while (sendStack.IsNotEmpty) { - sendStack.Pop().UnsafeAs().Invoke(); + sendStack.Pop().Invoke(); } // Catch all exceptions and continue executing callbacks until all are exhausted, then if there are any, throw all exceptions wrapped in AggregateException. List exceptions = null; for (int i = 0; i < postCount; ++i) { + ref var callback = ref postQueue[i]; try { - postQueue[i].Invoke(); - postQueue[i] = default; + callback.Invoke(); } catch (Exception e) { Internal.RecordException(e, ref exceptions); } + callback = default; } if (exceptions != null) diff --git a/Package/Core/Utilities/Internal/AsyncLazyInternal.cs b/Package/Core/Utilities/Internal/AsyncLazyInternal.cs index d8a9c490..7288521d 100644 --- a/Package/Core/Utilities/Internal/AsyncLazyInternal.cs +++ b/Package/Core/Utilities/Internal/AsyncLazyInternal.cs @@ -84,7 +84,8 @@ internal override Promise GetOrStartPromise(AsyncLazy owner, ProgressToken private sealed class LazyPromiseNoProgress : Internal.PromiseRefBase.LazyPromise { private AsyncLazy _owner; - internal PromiseMultiAwait _preservedPromise; + // TODO: make LazyPromise implement multi await handling directly, instead of allocating a separate object. + internal PromiseRetainer _promiseRetainer; [MethodImpl(Internal.InlineOption)] private static LazyPromiseNoProgress GetOrCreate() @@ -113,7 +114,7 @@ internal override void MaybeDispose() internal static Promise GetOrStartPromise(AsyncLazy owner, LazyFieldsNoProgress lazyFields) { LazyPromiseNoProgress lazyPromise; - PromiseMultiAwait preservedPromise; + PromiseRetainer promiseRetainer; lock (lazyFields) { if (lazyFields.IsComplete) @@ -123,16 +124,17 @@ internal static Promise GetOrStartPromise(AsyncLazy owner, LazyFieldsNoPro if (lazyFields.IsStarted) { - return GetDuplicate(lazyFields._lazyPromise.UnsafeAs()._preservedPromise); + promiseRetainer = lazyFields._lazyPromise.UnsafeAs()._promiseRetainer; + return promiseRetainer.WaitAsyncSkipValidation(); } lazyPromise = GetOrCreate(owner); lazyFields._lazyPromise = lazyPromise; - // Same thing as Promise.Preserve(), but more direct. - lazyPromise._preservedPromise = preservedPromise = PromiseMultiAwait.GetOrCreateAndHookup(lazyPromise, lazyPromise.Id); + // Same thing as Promise.GetRetainer(), but more direct. + lazyPromise._promiseRetainer = promiseRetainer = PromiseRetainer.GetOrCreateAndHookup(lazyPromise, lazyPromise.Id); // Exit the lock before invoking the factory. } - var promise = GetDuplicate(preservedPromise); + var promise = promiseRetainer.WaitAsyncSkipValidation(); lazyPromise.Start(lazyFields._factory); return promise; } @@ -150,18 +152,18 @@ internal override void Handle(Internal.PromiseRefBase handler, Promise.State sta protected override void OnComplete(Promise.State state) { var lazyFields = _owner._lazyFields; - PromiseMultiAwait preservedPromise; + PromiseRetainer promiseRetainer; if (state != Promises.Promise.State.Resolved) { lock (lazyFields) { // Reset the state so that the factory will be ran again the next time the Promise is accessed. - preservedPromise = _preservedPromise; - _preservedPromise = null; + promiseRetainer = _promiseRetainer; + _promiseRetainer = null; lazyFields._lazyPromise = null; } - preservedPromise.Forget(preservedPromise.Id); + promiseRetainer.Dispose(promiseRetainer.Id); HandleNextInternal(state); return; } @@ -173,12 +175,12 @@ protected override void OnComplete(Promise.State state) lock (lazyFields) { - preservedPromise = _preservedPromise; - _preservedPromise = null; + promiseRetainer = _promiseRetainer; + _promiseRetainer = null; lazyFields.UnsafeAs()._factory = null; } - preservedPromise.Forget(preservedPromise.Id); + promiseRetainer.Dispose(promiseRetainer.Id); HandleNextInternal(state); } } // class LazyPromiseNoProgress @@ -189,7 +191,7 @@ protected override void OnComplete(Promise.State state) private sealed class LazyWithProgressPromise : Internal.PromiseRefBase.LazyPromise { private AsyncLazy _owner; - internal PromiseMultiAwait _preservedPromise; + internal PromiseRetainer _promiseRetainer; internal Internal.ProgressMultiHandler _progressHandler; [MethodImpl(Internal.InlineOption)] @@ -219,7 +221,7 @@ internal override void MaybeDispose() internal static Promise GetOrStartPromise(AsyncLazy owner, LazyFieldsWithProgress lazyFields, ProgressToken progressToken) { LazyWithProgressPromise lazyPromise; - PromiseMultiAwait preservedPromise; + PromiseRetainer promiseRetainer; lock (lazyFields) { if (lazyFields.IsComplete) @@ -232,18 +234,19 @@ internal static Promise GetOrStartPromise(AsyncLazy owner, LazyFieldsWithP { var castedPromise = lazyFields._lazyPromise.UnsafeAs(); castedPromise._progressHandler.Add(progressToken, castedPromise._progressHandler.Id); - return GetDuplicate(castedPromise._preservedPromise); + promiseRetainer = castedPromise._promiseRetainer; + return promiseRetainer.WaitAsyncSkipValidation(); } lazyPromise = GetOrCreate(owner); lazyFields._lazyPromise = lazyPromise; // Same thing as Progress.NewMultiHandler(), but more direct. lazyPromise._progressHandler = Internal.ProgressMultiHandler.GetOrCreate(); - // Same thing as Promise.Preserve(), but more direct. - lazyPromise._preservedPromise = preservedPromise = PromiseMultiAwait.GetOrCreateAndHookup(lazyPromise, lazyPromise.Id); + // Same thing as Promise.GetRetainer(), but more direct. + lazyPromise._promiseRetainer = promiseRetainer = PromiseRetainer.GetOrCreateAndHookup(lazyPromise, lazyPromise.Id); // Exit the lock before invoking the factory. } - var promise = GetDuplicate(preservedPromise); + var promise = promiseRetainer.WaitAsyncSkipValidation(); lazyPromise._progressHandler.Add(progressToken, lazyPromise._progressHandler.Id); lazyPromise.Start(lazyFields._factory, new ProgressToken(lazyPromise._progressHandler, lazyPromise._progressHandler.Id, 0d, 1d)); return promise; @@ -266,22 +269,22 @@ internal override void Handle(Internal.PromiseRefBase handler, Promise.State sta protected override void OnComplete(Promise.State state) { var lazyFields = _owner._lazyFields; - PromiseMultiAwait preservedPromise; + PromiseRetainer promiseRetainer; Internal.ProgressMultiHandler progressHandler; if (state != Promises.Promise.State.Resolved) { lock (lazyFields) { // Reset the state so that the factory will be ran again the next time GetResultAsync is called. - preservedPromise = _preservedPromise; - _preservedPromise = null; + promiseRetainer = _promiseRetainer; + _promiseRetainer = null; progressHandler = _progressHandler; _progressHandler = null; lazyFields._lazyPromise = null; } progressHandler.Dispose(progressHandler.Id); - preservedPromise.Forget(preservedPromise.Id); + promiseRetainer.Dispose(promiseRetainer.Id); HandleNextInternal(state); return; } @@ -293,8 +296,8 @@ protected override void OnComplete(Promise.State state) lock (lazyFields) { - preservedPromise = _preservedPromise; - _preservedPromise = null; + promiseRetainer = _promiseRetainer; + _promiseRetainer = null; progressHandler = _progressHandler; _progressHandler = null; lazyFields.UnsafeAs()._factory = null; @@ -302,7 +305,7 @@ protected override void OnComplete(Promise.State state) progressHandler.Report(1d, progressHandler.Id); progressHandler.Dispose(progressHandler.Id); - preservedPromise.Forget(preservedPromise.Id); + promiseRetainer.Dispose(promiseRetainer.Id); HandleNextInternal(state); } } // class LazyWithProgressPromise @@ -317,14 +320,6 @@ partial class PromiseRefBase #endif internal abstract class LazyPromise : PromiseWaitPromise { - protected static Promise GetDuplicate(PromiseMultiAwait preservedPromise) - { - // Same thing as Promise.Duplicate(), but more direct. - var p = preservedPromise; - var duplicate = p.GetDuplicateT(p.Id); - return new Promise(duplicate, duplicate.Id); - } - [MethodImpl(InlineOption)] protected void Start(Func> factory) { @@ -332,7 +327,7 @@ protected void Start(Func> factory) try { var promise = factory.Invoke(); - WaitFor(promise._ref, promise._result, promise._id, null, new CompleteHandler(this)); + WaitFor(promise._ref, promise._result, promise._id, new CompleteHandler(this)); } catch (OperationCanceledException) { @@ -353,7 +348,7 @@ protected void Start(Func> factory, ProgressToke try { var promise = factory.Invoke(progressToken); - WaitFor(promise._ref, promise._result, promise._id, null, new CompleteHandler(this)); + WaitFor(promise._ref, promise._result, promise._id, new CompleteHandler(this)); } catch (OperationCanceledException) { diff --git a/Package/Core/Utilities/Progress.cs b/Package/Core/Utilities/Progress.cs index 3c372f6b..74405caf 100644 --- a/Package/Core/Utilities/Progress.cs +++ b/Package/Core/Utilities/Progress.cs @@ -264,7 +264,7 @@ public static MergeBuilder NewMergeBuilder(ProgressToken target) /// Create a new progress handler that can report to multiple progress tokens. /// /// - /// This can be useful when combined with for reporting progress to multiple consumers from a single async operation. + /// This can be useful when combined with for reporting progress to multiple consumers from a single async operation. /// /// A new progress multi handler. public static MultiHandler NewMultiHandler() @@ -402,7 +402,7 @@ private MultiHandler(Internal.ProgressMultiHandler impl) /// Create a new progress handler that can report to multiple progress tokens. /// /// - /// This can be useful when combined with for reporting progress to multiple consumers from a single async operation. + /// This can be useful when combined with for reporting progress to multiple consumers from a single async operation. /// /// A new progress multi handler. [MethodImpl(Internal.InlineOption)] diff --git a/Package/README.md b/Package/README.md index 3716b434..cfdd5161 100644 --- a/Package/README.md +++ b/Package/README.md @@ -11,12 +11,12 @@ Robust and efficient library for management of asynchronous operations. - Cancelable operations with custom allocation-free CancelationToken/Source - Allocation-free async iterators with async Linq - Progress with enforced normalization +- Structured concurrency - async/await support and .Then API - Thread safe -- Full causality traces - Easily switch to foreground or background context -- Combine async operations - Circular await detection +- Full causality traces - Interoperable with Tasks and Unity's Coroutines/Awaitables - CLS compliant @@ -49,11 +49,12 @@ See the [C# Asynchronous Benchmarks Repo](https://github.com/timcassell/CSharpAs ## Latest Updates -### v3.0.2 - April 12, 2024 +### v3.1.0 - June 30, 2024 -- Fixed `Promise.ParallelFor*` canceling workers too early. -- `Promise.ParallelFor*` and `AsyncEnumerable.Merge` propagate exceptions from cancelation token callbacks instead of deadlocking. -- `AsyncEnumerable.Merge` more eagerly stops iteration if a rejection or cancelation occurs. +- Added structured concurrency promise groups. +- Added `Promise().Each` APIs. +- Added `Promise().GetRetainer` APIs. +- Deprecated `Promise().{Preserve, Duplicate}` APIs. See [ChangeLog](https://github.com/timcassell/ProtoPromise/tree/master/Docs/Changelog) for the full changelog. diff --git a/Package/Samples~/Demo.unity b/Package/Samples~/Demo.unity index 2628dd75..2c63b401 100644 --- a/Package/Samples~/Demo.unity +++ b/Package/Samples~/Demo.unity @@ -784,7 +784,6 @@ GameObject: m_Component: - component: {fileID: 534669905} - component: {fileID: 534669904} - - component: {fileID: 534669903} m_Layer: 0 m_Name: Main Camera m_TagString: MainCamera @@ -792,14 +791,6 @@ GameObject: m_NavMeshLayer: 0 m_StaticEditorFlags: 0 m_IsActive: 1 ---- !u!81 &534669903 -AudioListener: - m_ObjectHideFlags: 0 - m_CorrespondingSourceObject: {fileID: 0} - m_PrefabInstance: {fileID: 0} - m_PrefabAsset: {fileID: 0} - m_GameObject: {fileID: 534669902} - m_Enabled: 1 --- !u!20 &534669904 Camera: m_ObjectHideFlags: 0 diff --git a/Package/Tests/CoreTests/APIs/APlus_2_1_PromiseStates.cs b/Package/Tests/CoreTests/APIs/APlus_2_1_PromiseStates.cs index 2903dc88..a3bbc57a 100644 --- a/Package/Tests/CoreTests/APIs/APlus_2_1_PromiseStates.cs +++ b/Package/Tests/CoreTests/APIs/APlus_2_1_PromiseStates.cs @@ -110,29 +110,28 @@ public void _2_1_2_1_MustNotTransitionToAnyOtherState_void() string RejectValue = "Rejected"; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - bool voidResolved = false, voidRejected = false; - promise - .Then(() => voidResolved = true, () => voidRejected = true) - .Forget(); - - deferred.Resolve(); - Assert.IsTrue(voidResolved); - Assert.IsFalse(voidRejected); - - Assert.IsFalse(deferred.TryResolve()); - Assert.Throws(() => deferred.Resolve()); - Assert.IsFalse(deferred.TryReject(RejectValue)); - Assert.Throws(() => deferred.Reject(RejectValue)); - - promise - .Then(() => voidResolved = true, () => voidRejected = true) - .Forget(); - Assert.IsTrue(voidResolved); - Assert.IsFalse(voidRejected); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + bool voidResolved = false, voidRejected = false; + promiseRetainer.WaitAsync() + .Then(() => voidResolved = true, () => voidRejected = true) + .Forget(); + + deferred.Resolve(); + Assert.IsTrue(voidResolved); + Assert.IsFalse(voidRejected); + + Assert.IsFalse(deferred.TryResolve()); + Assert.Throws(() => deferred.Resolve()); + Assert.IsFalse(deferred.TryReject(RejectValue)); + Assert.Throws(() => deferred.Reject(RejectValue)); + + promiseRetainer.WaitAsync() + .Then(() => voidResolved = true, () => voidRejected = true) + .Forget(); + Assert.IsTrue(voidResolved); + Assert.IsFalse(voidRejected); + } } [Test] @@ -141,59 +140,58 @@ public void _2_1_2_1_MustNotTransitionToAnyOtherState_T() string RejectValue = "Rejected"; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - bool intResolved = false, intRejected = false; - promise - .Then(_ => intResolved = true, () => intRejected = true) - .Forget(); - - deferred.Resolve(1); - Assert.IsTrue(intResolved); - Assert.IsFalse(intRejected); - - Assert.IsFalse(deferred.TryResolve(1)); - Assert.Throws(() => deferred.Resolve(1)); - Assert.IsFalse(deferred.TryReject(RejectValue)); - Assert.Throws(() => deferred.Reject(RejectValue)); - - promise - .Then(_ => intResolved = true, () => intRejected = true) - .Forget(); - Assert.IsTrue(intResolved); - Assert.IsFalse(intRejected); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + bool intResolved = false, intRejected = false; + promiseRetainer.WaitAsync() + .Then(_ => intResolved = true, () => intRejected = true) + .Forget(); + + deferred.Resolve(1); + Assert.IsTrue(intResolved); + Assert.IsFalse(intRejected); + + Assert.IsFalse(deferred.TryResolve(1)); + Assert.Throws(() => deferred.Resolve(1)); + Assert.IsFalse(deferred.TryReject(RejectValue)); + Assert.Throws(() => deferred.Reject(RejectValue)); + + promiseRetainer.WaitAsync() + .Then(_ => intResolved = true, () => intRejected = true) + .Forget(); + Assert.IsTrue(intResolved); + Assert.IsFalse(intRejected); + } } [Test] public void _2_1_2_2_MustHaveAValueWhichMustNotChange() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int result = -1; - int expected = 1; - - TestHelper.AddCallbacks(promise, - onResolve: num => { Assert.AreEqual(expected, num); result = num; }, - onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), - onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - deferred.Resolve(expected); - - Assert.AreEqual(expected, result); - - TestHelper.AddCallbacks(promise, - onResolve: num => { Assert.AreEqual(expected, num); result = num; }, - onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), - onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - Assert.IsFalse(deferred.TryResolve(100)); - Assert.Throws(() => deferred.Resolve(100)); - - Assert.AreEqual(expected, result); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + int result = -1; + int expected = 1; + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: num => { Assert.AreEqual(expected, num); result = num; }, + onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), + onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + deferred.Resolve(expected); + + Assert.AreEqual(expected, result); + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: num => { Assert.AreEqual(expected, num); result = num; }, + onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), + onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + Assert.IsFalse(deferred.TryResolve(100)); + Assert.Throws(() => deferred.Resolve(100)); + + Assert.AreEqual(expected, result); + } } } @@ -217,29 +215,28 @@ public void _2_1_3_1_MustNotTransitionToAnyOtherState_void() string RejectValue = "Rejected"; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - bool voidResolved = false, voidRejected = false; - promise - .Then(() => voidResolved = true, () => voidRejected = true) - .Forget(); - - deferred.Reject(RejectValue); - Assert.IsFalse(voidResolved); - Assert.IsTrue(voidRejected); - - Assert.IsFalse(deferred.TryResolve()); - Assert.Throws(() => deferred.Resolve()); - Assert.IsFalse(deferred.TryReject(RejectValue)); - Assert.Throws(() => deferred.Reject(RejectValue)); - - promise - .Then(() => voidResolved = true, () => voidRejected = true) - .Forget(); - Assert.IsFalse(voidResolved); - Assert.IsTrue(voidRejected); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + bool voidResolved = false, voidRejected = false; + promiseRetainer.WaitAsync() + .Then(() => voidResolved = true, () => voidRejected = true) + .Forget(); + + deferred.Reject(RejectValue); + Assert.IsFalse(voidResolved); + Assert.IsTrue(voidRejected); + + Assert.IsFalse(deferred.TryResolve()); + Assert.Throws(() => deferred.Resolve()); + Assert.IsFalse(deferred.TryReject(RejectValue)); + Assert.Throws(() => deferred.Reject(RejectValue)); + + promiseRetainer.WaitAsync() + .Then(() => voidResolved = true, () => voidRejected = true) + .Forget(); + Assert.IsFalse(voidResolved); + Assert.IsTrue(voidRejected); + } } [Test] @@ -248,96 +245,94 @@ public void _2_1_3_1_MustNotTransitionToAnyOtherState_T() string RejectValue = "Rejected"; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - bool intResolved = false, intRejected = false; - promise - .Then(_ => intResolved = true, () => intRejected = true) - .Forget(); - - deferred.Reject(RejectValue); - Assert.IsFalse(intResolved); - Assert.IsTrue(intRejected); - - Assert.IsFalse(deferred.TryResolve(1)); - Assert.Throws(() => deferred.Resolve(1)); - Assert.IsFalse(deferred.TryReject(RejectValue)); - Assert.Throws(() => deferred.Reject(RejectValue)); - - promise - .Then(_ => intResolved = true, () => intRejected = true) - .Forget(); - Assert.IsFalse(intResolved); - Assert.IsTrue(intRejected); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + bool intResolved = false, intRejected = false; + promiseRetainer.WaitAsync() + .Then(_ => intResolved = true, () => intRejected = true) + .Forget(); + + deferred.Reject(RejectValue); + Assert.IsFalse(intResolved); + Assert.IsTrue(intRejected); + + Assert.IsFalse(deferred.TryResolve(1)); + Assert.Throws(() => deferred.Resolve(1)); + Assert.IsFalse(deferred.TryReject(RejectValue)); + Assert.Throws(() => deferred.Reject(RejectValue)); + + promiseRetainer.WaitAsync() + .Then(_ => intResolved = true, () => intRejected = true) + .Forget(); + Assert.IsFalse(intResolved); + Assert.IsTrue(intRejected); + } } [Test] public void _2_1_3_2_MustHaveAReasonWhichMustNotChange_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string rejection = null; - string expected = "Fail Value"; - TestHelper.AddCallbacks(promise, - onResolve: () => Assert.Fail("Promise was resolved when it should have been rejected."), - onReject: failValue => - { - Assert.AreEqual(expected, failValue); - rejection = failValue; - }); - deferred.Reject(expected); - - Assert.AreEqual(expected, rejection); - - Assert.IsFalse(deferred.TryReject("Different Fail Value")); - Assert.Throws(() => deferred.Reject("Different Fail Value")); - TestHelper.AddCallbacks(promise, - onResolve: () => Assert.Fail("Promise was resolved when it should have been rejected."), - onReject: failValue => - { - Assert.AreEqual(expected, failValue); - rejection = failValue; - }); - - Assert.AreEqual(expected, rejection); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + string rejection = null; + string expected = "Fail Value"; + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => Assert.Fail("Promise was resolved when it should have been rejected."), + onReject: failValue => + { + Assert.AreEqual(expected, failValue); + rejection = failValue; + }); + deferred.Reject(expected); + + Assert.AreEqual(expected, rejection); + + Assert.IsFalse(deferred.TryReject("Different Fail Value")); + Assert.Throws(() => deferred.Reject("Different Fail Value")); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => Assert.Fail("Promise was resolved when it should have been rejected."), + onReject: failValue => + { + Assert.AreEqual(expected, failValue); + rejection = failValue; + }); + + Assert.AreEqual(expected, rejection); + } } [Test] public void _2_1_3_2_MustHaveAReasonWhichMustNotChange_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string rejection = null; - string expected = "Fail Value"; - TestHelper.AddCallbacks(promise, - onResolve: v => Assert.Fail("Promise was resolved when it should have been rejected."), - onReject: failValue => - { - Assert.AreEqual(expected, failValue); - rejection = failValue; - }); - deferred.Reject(expected); - - Assert.AreEqual(expected, rejection); - - Assert.IsFalse(deferred.TryReject("Different Fail Value")); - Assert.Throws(() => deferred.Reject("Different Fail Value")); - TestHelper.AddCallbacks(promise, - onResolve: v => Assert.Fail("Promise was resolved when it should have been rejected."), - onReject: failValue => - { - Assert.AreEqual(expected, failValue); - rejection = failValue; - }); - - Assert.AreEqual(expected, rejection); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + string rejection = null; + string expected = "Fail Value"; + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => Assert.Fail("Promise was resolved when it should have been rejected."), + onReject: failValue => + { + Assert.AreEqual(expected, failValue); + rejection = failValue; + }); + deferred.Reject(expected); + + Assert.AreEqual(expected, rejection); + + Assert.IsFalse(deferred.TryReject("Different Fail Value")); + Assert.Throws(() => deferred.Reject("Different Fail Value")); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => Assert.Fail("Promise was resolved when it should have been rejected."), + onReject: failValue => + { + Assert.AreEqual(expected, failValue); + rejection = failValue; + }); + + Assert.AreEqual(expected, rejection); + } } } } diff --git a/Package/Tests/CoreTests/APIs/APlus_2_2_TheThenMethod.cs b/Package/Tests/CoreTests/APIs/APlus_2_2_TheThenMethod.cs index f53dae43..836742f5 100644 --- a/Package/Tests/CoreTests/APIs/APlus_2_2_TheThenMethod.cs +++ b/Package/Tests/CoreTests/APIs/APlus_2_2_TheThenMethod.cs @@ -46,101 +46,38 @@ public void Teardown() public void _2_2_1_1_IfOnFulfilledIsNull_Throw_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Then(default(Action)); - }); - Assert.Throws(() => - { - promise.Then(default(Func)); - }); - Assert.Throws(() => - { - promise.Then(default(Func)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>)); - }); + Assert.Throws(() => promise.Then(default(Action))); + Assert.Throws(() => promise.Then(default(Func))); + Assert.Throws(() => promise.Then(default(Func))); + Assert.Throws(() => promise.Then(default(Func>))); + Assert.Throws(() => promise.Then(default(Action), () => { })); + Assert.Throws(() => promise.Then(default(Action), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(default(Action), () => { }); - }); - Assert.Throws(() => - { - promise.Then(default(Action), (string failValue) => { }); - }); + Assert.Throws(() => promise.Then(default(Func), () => default(int))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(int))); - Assert.Throws(() => - { - promise.Then(default(Func), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Func>), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func>), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func>), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>), (string failValue) => default(Promise)); - }); - - - Assert.Throws(() => - { - promise.Then(default(Action), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Action), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Action), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Action), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func), () => { }); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => { }); - }); + Assert.Throws(() => promise.Then(default(Func), () => { })); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(default(Func>), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(default(Func>), () => default(int))); + Assert.Throws(() => promise.Then(default(Func>), (string failValue) => default(int))); deferred.Resolve(); - promise.Forget(); } @@ -148,101 +85,38 @@ public void _2_2_1_1_IfOnFulfilledIsNull_Throw_void() public void _2_2_1_1_IfOnFulfilledIsNull_Throw_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Then(default(Action)); - }); - Assert.Throws(() => - { - promise.Then(default(Func)); - }); - Assert.Throws(() => - { - promise.Then(default(Func)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>)); - }); + Assert.Throws(() => promise.Then(default(Action))); + Assert.Throws(() => promise.Then(default(Func))); + Assert.Throws(() => promise.Then(default(Func))); + Assert.Throws(() => promise.Then(default(Func>))); + Assert.Throws(() => promise.Then(default(Action), () => { })); + Assert.Throws(() => promise.Then(default(Action), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(default(Action), () => { }); - }); - Assert.Throws(() => - { - promise.Then(default(Action), (string failValue) => { }); - }); + Assert.Throws(() => promise.Then(default(Func), () => default(int))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(int))); - Assert.Throws(() => - { - promise.Then(default(Func), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Func>), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func>), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Func>), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Action), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Action), (string failValue) => default(Promise))); + Assert.Throws(() => promise.Then(default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(default(Action), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Action), (string failValue) => default(Promise)); - }); - - Assert.Throws(() => - { - promise.Then(default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(default(Func), () => { })); + Assert.Throws(() => promise.Then(default(Func), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(default(Func), () => { }); - }); - Assert.Throws(() => - { - promise.Then(default(Func), (string failValue) => { }); - }); - - Assert.Throws(() => - { - promise.Then(default(Func>), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(default(Func>), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(default(Func>), () => default(int))); + Assert.Throws(() => promise.Then(default(Func>), (string failValue) => default(int))); deferred.Resolve(0); - promise.Forget(); } @@ -250,102 +124,39 @@ public void _2_2_1_1_IfOnFulfilledIsNull_Throw_T() public void _2_2_1_2_IfOnRejectedIsNull_Throw_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Catch(default(Action)); - }); - Assert.Throws(() => - { - promise.Catch(default(Action)); - }); + Assert.Throws(() => promise.Catch(default(Action))); + Assert.Throws(() => promise.Catch(default(Action))); - Assert.Throws(() => - { - promise.Catch(default(Func)); - }); - Assert.Throws(() => - { - promise.Catch(default(Func)); - }); + Assert.Throws(() => promise.Catch(default(Func))); + Assert.Throws(() => promise.Catch(default(Func))); + Assert.Throws(() => promise.Then(() => { }, default(Action))); + Assert.Throws(() => promise.Then(() => { }, default(Action))); - Assert.Throws(() => - { - promise.Then(() => { }, default(Action)); - }); - Assert.Throws(() => - { - promise.Then(() => { }, default(Action)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func))); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func)); - }); + Assert.Throws(() => promise.Then(() => "string", default(Func))); + Assert.Throws(() => promise.Then(() => "string", default(Func))); - Assert.Throws(() => - { - promise.Then(() => "string", default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => "string", default(Func)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func>))); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func>))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func>)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func>)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), default(Action))); + Assert.Throws(() => promise.Then(() => default(Promise), default(Action))); + Assert.Throws(() => promise.Then(() => { }, default(Func))); + Assert.Throws(() => promise.Then(() => { }, default(Func))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Action)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Action)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func))); + Assert.Throws(() => promise.Then(() => default(Promise), default(Func))); - Assert.Throws(() => - { - promise.Then(() => { }, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => { }, default(Func)); - }); - - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), default(Func)); - }); - - Assert.Throws(() => - { - promise.Then(() => "string", default(Func>)); - }); - Assert.Throws(() => - { - promise.Then(() => "string", default(Func>)); - }); + Assert.Throws(() => promise.Then(() => "string", default(Func>))); + Assert.Throws(() => promise.Then(() => "string", default(Func>))); deferred.Resolve(); - promise.Forget(); } @@ -353,102 +164,39 @@ public void _2_2_1_2_IfOnRejectedIsNull_Throw_void() public void _2_2_1_2_IfOnRejectedIsNull_Throw_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Catch(default(Func)); - }); - Assert.Throws(() => - { - promise.Catch(default(Func)); - }); + Assert.Throws(() => promise.Catch(default(Func))); + Assert.Throws(() => promise.Catch(default(Func))); - Assert.Throws(() => - { - promise.Catch(default(Func>)); - }); - Assert.Throws(() => - { - promise.Catch(default(Func>)); - }); + Assert.Throws(() => promise.Catch(default(Func>))); + Assert.Throws(() => promise.Catch(default(Func>))); + Assert.Throws(() => promise.Then((int x) => { }, default(Action))); + Assert.Throws(() => promise.Then((int x) => { }, default(Action))); - Assert.Throws(() => - { - promise.Then((int x) => { }, default(Action)); - }); - Assert.Throws(() => - { - promise.Then((int x) => { }, default(Action)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func))); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func)); - }); + Assert.Throws(() => promise.Then((int x) => "string", default(Func))); + Assert.Throws(() => promise.Then((int x) => "string", default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => "string", default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => "string", default(Func)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func>))); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func>))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func>)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func>)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Action))); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Action))); + Assert.Throws(() => promise.Then((int x) => { }, default(Func))); + Assert.Throws(() => promise.Then((int x) => { }, default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Action)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Action)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func))); + Assert.Throws(() => promise.Then((int x) => default(Promise), default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => { }, default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => { }, default(Func)); - }); - - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), default(Func)); - }); - - Assert.Throws(() => - { - promise.Then((int x) => "string", default(Func>)); - }); - Assert.Throws(() => - { - promise.Then((int x) => "string", default(Func>)); - }); + Assert.Throws(() => promise.Then((int x) => "string", default(Func>))); + Assert.Throws(() => promise.Then((int x) => "string", default(Func>))); deferred.Resolve(0); - promise.Forget(); } } @@ -474,27 +222,26 @@ public void _2_2_2_1_ItMustBeCalledAfterPromiseIsFulfilledWithPromisesValueAsIts var promisedValue = 100; var resolved = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: v => - { - Assert.AreEqual(promisedValue, v); - resolved = true; - } - ); - TestHelper.AddCallbacks(promise, - onResolve: v => - { - Assert.AreEqual(promisedValue, v); - resolved = true; - } - ); - deferred.Resolve(promisedValue); - - Assert.True(resolved); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => + { + Assert.AreEqual(promisedValue, v); + resolved = true; + } + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => + { + Assert.AreEqual(promisedValue, v); + resolved = true; + } + ); + deferred.Resolve(promisedValue); + + Assert.True(resolved); + } } [Test] @@ -502,24 +249,23 @@ public void _2_2_2_2_ItMustNotBeCalledBeforePromiseIsFulfilled_void() { var resolved = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), () => resolved = true ); - TestHelper.AddCallbacks(promise, - () => resolved = true, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + () => resolved = true, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); - Assert.False(resolved); + Assert.False(resolved); - deferred.Resolve(); + deferred.Resolve(); - Assert.True(resolved); - - promise.Forget(); + Assert.True(resolved); + } } [Test] @@ -527,24 +273,23 @@ public void _2_2_2_2_ItMustNotBeCalledBeforePromiseIsFulfilled_T() { var resolved = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - v => resolved = true - ); - TestHelper.AddCallbacks(promise, - v => resolved = true, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - - Assert.False(resolved); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + v => resolved = true + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + v => resolved = true, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); - deferred.Resolve(100); + Assert.False(resolved); - Assert.True(resolved); + deferred.Resolve(100); - promise.Forget(); + Assert.True(resolved); + } } [Test] @@ -552,28 +297,27 @@ public void _2_2_2_3_ItMustNotBeCalledMoreThanOnce_void() { var resolveCount = 0; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - () => ++resolveCount - ); - TestHelper.AddCallbacks(promise, - () => ++resolveCount, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - deferred.Resolve(); - - Assert.IsFalse(deferred.TryResolve()); - Assert.Throws(() => deferred.Resolve()); - - Assert.AreEqual( - (TestHelper.resolveVoidVoidCallbacks + TestHelper.resolveVoidConvertCallbacks + - TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks) * 2, - resolveCount - ); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + () => ++resolveCount + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + () => ++resolveCount, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + deferred.Resolve(); + + Assert.IsFalse(deferred.TryResolve()); + Assert.Throws(() => deferred.Resolve()); + + Assert.AreEqual( + (TestHelper.resolveVoidVoidCallbacks + TestHelper.resolveVoidConvertCallbacks + + TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks) * 2, + resolveCount + ); + } } [Test] @@ -581,28 +325,27 @@ public void _2_2_2_3_ItMustNotBeCalledMoreThanOnce_T() { var resolveCount = 0; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - x => ++resolveCount - ); - TestHelper.AddCallbacks(promise, - x => ++resolveCount, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - () => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - deferred.Resolve(1); - - Assert.IsFalse(deferred.TryResolve(1)); - Assert.Throws(() => deferred.Resolve(100)); - - Assert.AreEqual( - (TestHelper.resolveTVoidCallbacks + TestHelper.resolveTConvertCallbacks + - TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks) * 2, - resolveCount - ); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + x => ++resolveCount + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + x => ++resolveCount, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + () => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + deferred.Resolve(1); + + Assert.IsFalse(deferred.TryResolve(1)); + Assert.Throws(() => deferred.Resolve(100)); + + Assert.AreEqual( + (TestHelper.resolveTVoidCallbacks + TestHelper.resolveTConvertCallbacks + + TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks) * 2, + resolveCount + ); + } } } @@ -751,26 +494,25 @@ public void _2_2_4_OnFulfilledMustNotBeCalledUntilTheExecutionContextStackContai { bool resolved = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - () => resolved = true, - configureAwaitType: ConfigureAwaitType.Foreground, - configureAwaitForceAsync: true - ); - TestHelper.AddCallbacks(promise, - () => resolved = true, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - configureAwaitType: ConfigureAwaitType.Foreground, - configureAwaitForceAsync: true - ); - deferred.Resolve(); - Assert.False(resolved); - - TestHelper.ExecuteForegroundCallbacks(); - Assert.True(resolved); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + () => resolved = true, + configureAwaitType: ConfigureAwaitType.Foreground, + configureAwaitForceAsync: true + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + () => resolved = true, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + configureAwaitType: ConfigureAwaitType.Foreground, + configureAwaitForceAsync: true + ); + deferred.Resolve(); + Assert.False(resolved); - promise.Forget(); + TestHelper.ExecuteForegroundCallbacks(); + Assert.True(resolved); + } } [Test] @@ -778,26 +520,25 @@ public void _2_2_4_OnFulfilledMustNotBeCalledUntilTheExecutionContextStackContai { bool resolved = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddResolveCallbacks(promise, - v => resolved = true, - configureAwaitType: ConfigureAwaitType.Foreground, - configureAwaitForceAsync: true - ); - TestHelper.AddCallbacks(promise, - v => resolved = true, - s => Assert.Fail("Promise was rejected when it should have been resolved."), - configureAwaitType: ConfigureAwaitType.Foreground, - configureAwaitForceAsync: true - ); - deferred.Resolve(1); - Assert.False(resolved); - - TestHelper.ExecuteForegroundCallbacks(); - Assert.True(resolved); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + v => resolved = true, + configureAwaitType: ConfigureAwaitType.Foreground, + configureAwaitForceAsync: true + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + v => resolved = true, + s => Assert.Fail("Promise was rejected when it should have been resolved."), + configureAwaitType: ConfigureAwaitType.Foreground, + configureAwaitForceAsync: true + ); + deferred.Resolve(1); + Assert.False(resolved); - promise.Forget(); + TestHelper.ExecuteForegroundCallbacks(); + Assert.True(resolved); + } } [Test] @@ -859,84 +600,80 @@ public void Teardown() public void _2_2_6_1_IfWhenPromiseIsFulfilled_AllRespectiveOnFulfilledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToThen_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int expected = i; - promise - .Then(() => Assert.AreEqual(expected, counter++)) - .Forget(); - } - - deferred.Resolve(); - promise.Forget(); + int counter = 0; + for (int i = 0; i < 10; ++i) + { + int expected = i; + promiseRetainer.WaitAsync() + .Then(() => Assert.AreEqual(expected, counter++)) + .Forget(); + } - Assert.AreEqual(10, counter); + deferred.Resolve(); + Assert.AreEqual(10, counter); + } } [Test] public void _2_2_6_1_IfWhenPromiseIsFulfilled_AllRespectiveOnFulfilledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToThen_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int expected = i; - promise - .Then(v => Assert.AreEqual(expected, counter++)) - .Forget(); - } - - deferred.Resolve(100); - promise.Forget(); + int counter = 0; + for (int i = 0; i < 10; ++i) + { + int expected = i; + promiseRetainer.WaitAsync() + .Then(v => Assert.AreEqual(expected, counter++)) + .Forget(); + } - Assert.AreEqual(10, counter); + deferred.Resolve(100); + Assert.AreEqual(10, counter); + } } [Test] public void _2_2_6_2_IfWhenPromiseIsRejected_AllRespectiveOnRejectedCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToThenOrCatch_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int expected = i; - promise + int counter = 0; + for (int i = 0; i < 10; ++i) + { + int expected = i; + promiseRetainer.WaitAsync() .Catch(() => Assert.AreEqual(expected, counter++)) .Forget(); - } - - deferred.Reject("Fail value"); - promise.Forget(); + } - Assert.AreEqual(10, counter); + deferred.Reject("Fail value"); + Assert.AreEqual(10, counter); + } } [Test] public void _2_2_6_2_IfWhenPromiseIsRejected_AllRespectiveOnRejectedCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToThenOrCatch_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int expected = i; - promise + int counter = 0; + for (int i = 0; i < 10; ++i) + { + int expected = i; + promiseRetainer.WaitAsync() .Catch(() => Assert.AreEqual(expected, counter++)) .Forget(); - } - - deferred.Reject("Fail value"); - promise.Forget(); + } - Assert.AreEqual(10, counter); + deferred.Reject("Fail value"); + Assert.AreEqual(10, counter); + } } } @@ -960,85 +697,83 @@ public void Teardown() public void _2_2_7_2_IfOnFulfilledThrowsAnExceptionE_Promise2MustBeRejectedWithEAsTheReason_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int exceptionCount = 0; - Exception expected = new Exception("Fail value"); - - Action catchCallback = p => - p.Catch((Exception e) => - { - Assert.AreEqual(expected, e); - ++exceptionCount; - }).Forget(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: () => { throw expected; }, - onResolveCapture: _ => { throw expected; }, - onCallbackAdded: (ref Promise p) => catchCallback(p), - onCallbackAddedConvert: (ref Promise p) => catchCallback(p) - ); - TestHelper.AddCallbacks(promise, - onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), - onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved."), - onResolve: () => { throw expected; }, - onResolveCapture: _ => { throw expected; }, - onCallbackAdded: (ref Promise p) => catchCallback(p), - onCallbackAddedConvert: (ref Promise p) => catchCallback(p) - ); - - deferred.Resolve(); - - Assert.AreEqual( - (TestHelper.resolveVoidVoidCallbacks + TestHelper.resolveVoidConvertCallbacks + - TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks) * 2, - exceptionCount - ); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + int exceptionCount = 0; + Exception expected = new Exception("Fail value"); + + Action catchCallback = p => + p.Catch((Exception e) => + { + Assert.AreEqual(expected, e); + ++exceptionCount; + }).Forget(); + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw expected; }, + onResolveCapture: _ => { throw expected; }, + onCallbackAdded: (ref Promise p) => catchCallback(p), + onCallbackAddedConvert: (ref Promise p) => catchCallback(p) + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), + onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved."), + onResolve: () => { throw expected; }, + onResolveCapture: _ => { throw expected; }, + onCallbackAdded: (ref Promise p) => catchCallback(p), + onCallbackAddedConvert: (ref Promise p) => catchCallback(p) + ); + + deferred.Resolve(); + + Assert.AreEqual( + (TestHelper.resolveVoidVoidCallbacks + TestHelper.resolveVoidConvertCallbacks + + TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks) * 2, + exceptionCount + ); + } } [Test] public void _2_2_7_2_IfOnFulfilledThrowsAnExceptionE_Promise2MustBeRejectedWithEAsTheReason_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int exceptionCount = 0; - Exception expected = new Exception("Fail value"); - - Action catchCallback = p => - p.Catch((Exception e) => - { - Assert.AreEqual(expected, e); - ++exceptionCount; - }).Forget(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: v => { throw expected; }, - onResolveCapture: _ => { throw expected; }, - onCallbackAdded: (ref Promise p) => catchCallback(p), - onCallbackAddedConvert: (ref Promise p) => catchCallback(p) - ); - TestHelper.AddCallbacks(promise, - onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), - onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved."), - onResolve: v => { throw expected; }, - onResolveCapture: _ => { throw expected; }, - onCallbackAdded: (ref Promise p) => catchCallback(p), - onCallbackAddedConvert: (ref Promise p) => catchCallback(p), - onCallbackAddedT: (ref Promise p) => catchCallback(p) - ); - - deferred.Resolve(100); - - Assert.AreEqual( - (TestHelper.resolveTVoidCallbacks + TestHelper.resolveTConvertCallbacks + - TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks) * 2, - exceptionCount - ); - - promise.Forget(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + int exceptionCount = 0; + Exception expected = new Exception("Fail value"); + + Action catchCallback = p => + p.Catch((Exception e) => + { + Assert.AreEqual(expected, e); + ++exceptionCount; + }).Forget(); + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw expected; }, + onResolveCapture: _ => { throw expected; }, + onCallbackAdded: (ref Promise p) => catchCallback(p), + onCallbackAddedConvert: (ref Promise p) => catchCallback(p) + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: s => Assert.Fail("Promise was rejected when it should have been resolved."), + onUnknownRejection: () => Assert.Fail("Promise was rejected when it should have been resolved."), + onResolve: v => { throw expected; }, + onResolveCapture: _ => { throw expected; }, + onCallbackAdded: (ref Promise p) => catchCallback(p), + onCallbackAddedConvert: (ref Promise p) => catchCallback(p), + onCallbackAddedT: (ref Promise p) => catchCallback(p) + ); + + deferred.Resolve(100); + + Assert.AreEqual( + (TestHelper.resolveTVoidCallbacks + TestHelper.resolveTConvertCallbacks + + TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks) * 2, + exceptionCount + ); + } } [Test] @@ -1117,26 +852,25 @@ public void _2_2_7_3_IfOnFulfilledIsNotAFunctionAndPromise1IsFulfilled_Promise2M var deferred = Promise.NewDeferred(); var promise1 = deferred.Promise; - var promise2 = promise1 + using (var promiseRetainer2 = promise1 .Catch(() => { Assert.Fail("Promise was rejected when it should have been resolved."); return; }) - .Preserve(); - - TestHelper.AddResolveCallbacks(promise2, - () => ++counter - ); - TestHelper.AddCallbacks(promise2, - () => ++counter, - s => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - - deferred.Resolve(); - - Assert.AreEqual( - TestHelper.resolveVoidCallbacks * 2, - counter - ); - - promise2.Forget(); + .GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer2.WaitAsync(), + () => ++counter + ); + TestHelper.AddCallbacks(promiseRetainer2.WaitAsync(), + () => ++counter, + s => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + + deferred.Resolve(); + + Assert.AreEqual( + TestHelper.resolveVoidCallbacks * 2, + counter + ); + } } [Test] @@ -1146,26 +880,25 @@ public void _2_2_7_3_IfOnFulfilledIsNotAFunctionAndPromise1IsFulfilled_Promise2M var deferred = Promise.NewDeferred(); var promise1 = deferred.Promise; - var promise2 = promise1 + using (var promiseRetainer2 = promise1 .Catch(() => { Assert.Fail("Promise was rejected when it should have been resolved."); return Promise.Resolved(); }) - .Preserve(); - - TestHelper.AddResolveCallbacks(promise2, - () => ++counter - ); - TestHelper.AddCallbacks(promise2, - () => ++counter, - s => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - - deferred.Resolve(); - - Assert.AreEqual( - TestHelper.resolveVoidCallbacks * 2, - counter - ); - - promise2.Forget(); + .GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer2.WaitAsync(), + () => ++counter + ); + TestHelper.AddCallbacks(promiseRetainer2.WaitAsync(), + () => ++counter, + s => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + + deferred.Resolve(); + + Assert.AreEqual( + TestHelper.resolveVoidCallbacks * 2, + counter + ); + } } [Test] @@ -1176,26 +909,25 @@ public void _2_2_7_3_IfOnFulfilledIsNotAFunctionAndPromise1IsFulfilled_Promise2M var deferred = Promise.NewDeferred(); var promise1 = deferred.Promise; - var promise2 = promise1 + using (var promiseRetainer2 = promise1 .Catch(() => { Assert.Fail("Promise was rejected when it should have been resolved."); return 50; }) - .Preserve(); - - TestHelper.AddResolveCallbacks(promise2, - v => { Assert.AreEqual(expected, v); ++counter; } - ); - TestHelper.AddCallbacks(promise2, - v => { Assert.AreEqual(expected, v); ++counter; }, - s => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - - deferred.Resolve(expected); - - Assert.AreEqual( - TestHelper.resolveTCallbacks * 2, - counter - ); - - promise2.Forget(); + .GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer2.WaitAsync(), + v => { Assert.AreEqual(expected, v); ++counter; } + ); + TestHelper.AddCallbacks(promiseRetainer2.WaitAsync(), + v => { Assert.AreEqual(expected, v); ++counter; }, + s => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + + deferred.Resolve(expected); + + Assert.AreEqual( + TestHelper.resolveTCallbacks * 2, + counter + ); + } } [Test] @@ -1206,26 +938,25 @@ public void _2_2_7_3_IfOnFulfilledIsNotAFunctionAndPromise1IsFulfilled_Promise2M var deferred = Promise.NewDeferred(); var promise1 = deferred.Promise; - var promise2 = promise1 + using (var promiseRetainer2 = promise1 .Catch(() => { Assert.Fail("Promise was rejected when it should have been resolved."); return Promise.Resolved(50); }) - .Preserve(); - - TestHelper.AddResolveCallbacks(promise2, - v => { Assert.AreEqual(expected, v); ++counter; } - ); - TestHelper.AddCallbacks(promise2, - v => { Assert.AreEqual(expected, v); ++counter; }, - s => Assert.Fail("Promise was rejected when it should have been resolved.") - ); - - deferred.Resolve(expected); - - Assert.AreEqual( - TestHelper.resolveTCallbacks * 2, - counter - ); - - promise2.Forget(); + .GetRetainer()) + { + TestHelper.AddResolveCallbacks(promiseRetainer2.WaitAsync(), + v => { Assert.AreEqual(expected, v); ++counter; } + ); + TestHelper.AddCallbacks(promiseRetainer2.WaitAsync(), + v => { Assert.AreEqual(expected, v); ++counter; }, + s => Assert.Fail("Promise was rejected when it should have been resolved.") + ); + + deferred.Resolve(expected); + + Assert.AreEqual( + TestHelper.resolveTCallbacks * 2, + counter + ); + } } [Test] diff --git a/Package/Tests/CoreTests/APIs/APlus_2_3_ThePromiseResolutionProcedure.cs b/Package/Tests/CoreTests/APIs/APlus_2_3_ThePromiseResolutionProcedure.cs index e7eb061b..f461d4b2 100644 --- a/Package/Tests/CoreTests/APIs/APlus_2_3_ThePromiseResolutionProcedure.cs +++ b/Package/Tests/CoreTests/APIs/APlus_2_3_ThePromiseResolutionProcedure.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using Proto.Promises; using System; +using System.Collections.Generic; namespace ProtoPromiseTests.APIs { @@ -30,175 +31,133 @@ public void Teardown() [Test] public void _2_3_1_IfPromiseAndXReferToTheSameObject_RejectPromiseWithInvalidReturnExceptionAsTheReason_void() { + string expectedMessage = "A Promise cannot wait on itself."; int exceptionCounter = 0; - var resolveDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectDeferred = Promise.NewDeferred(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - TestAction catchCallback = (ref Promise p) => + // When the promise awaits itself, it becomes rejected and invalidated, so the InvalidReturnException gets sent to the UncaughtRejectionHandler. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + Promise.Config.UncaughtRejectionHandler = e => { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); + Assert.IsAssignableFrom(e.Value); + Assert.AreEqual(expectedMessage, e.Value.UnsafeAs().Message); + ++exceptionCounter; }; - TestAction> catchCallbackConvert = (ref Promise p) => + + var resolveDeferred = Promise.NewDeferred(); + var rejectDeferred = Promise.NewDeferred(); + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); - }; + int expectedCount = 0; + var voidPromiseQueue = new Queue(); + var intPromiseQueue = new Queue>(); + Func voidReturnProvider = () => voidPromiseQueue.Dequeue(); + Func> intReturnProvider = () => intPromiseQueue.Dequeue(); - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseVoidToVoid(voidReturnProvider)) + { + if (adoptLocation == AdoptLocation.Reject) continue; - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); + ++expectedCount; + voidPromiseQueue.Enqueue(func.Invoke(resolvePromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseVoidToT(intReturnProvider)) + { + if (adoptLocation == AdoptLocation.Reject) continue; - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); + ++expectedCount; + intPromiseQueue.Enqueue(func.Invoke(resolvePromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseVoidToVoid(voidReturnProvider)) + { + if (adoptLocation == AdoptLocation.Resolve) continue; - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); + ++expectedCount; + voidPromiseQueue.Enqueue(func.Invoke(rejectPromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseVoidToT(intReturnProvider)) + { + if (adoptLocation == AdoptLocation.Resolve) continue; + + ++expectedCount; + intPromiseQueue.Enqueue(func.Invoke(rejectPromiseRetainer.WaitAsync())); + } + + resolveDeferred.Resolve(); + rejectDeferred.Reject("Fail value"); + + Assert.AreEqual(expectedCount, exceptionCounter); + } + } - resolvePromise.Forget(); - rejectPromise.Forget(); + Promise.Config.UncaughtRejectionHandler = currentHandler; } [Test] public void _2_3_1_IfPromiseAndXReferToTheSameObject_RejectPromiseWithInvalidReturnExceptionAsTheReason_T() { + string expectedMessage = "A Promise cannot wait on itself."; int exceptionCounter = 0; - var resolveDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectDeferred = Promise.NewDeferred(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - TestAction catchCallback = (ref Promise p) => + // When the promise awaits itself, it becomes rejected and invalidated, so the InvalidReturnException gets sent to the UncaughtRejectionHandler. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + Promise.Config.UncaughtRejectionHandler = e => { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); + Assert.IsAssignableFrom(e.Value); + Assert.AreEqual(expectedMessage, e.Value.UnsafeAs().Message); + ++exceptionCounter; }; - TestAction> catchCallbackConvert = (ref Promise p) => + + var resolveDeferred = Promise.NewDeferred(); + var rejectDeferred = Promise.NewDeferred(); + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); - }; + int expectedCount = 0; + var voidPromiseQueue = new Queue(); + var intPromiseQueue = new Queue>(); + Func voidReturnProvider = () => voidPromiseQueue.Dequeue(); + Func> intReturnProvider = () => intPromiseQueue.Dequeue(); - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - promiseToPromiseT: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert, - onCallbackAddedT: catchCallbackConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseTToVoid(voidReturnProvider)) + { + if (adoptLocation == AdoptLocation.Reject) continue; - TestHelper.AddCallbacks(rejectPromise, - onResolve: _ => resolveAssert(), - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - promiseToPromiseT: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert, - onCallbackAddedT: catchCallbackConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert - ); + ++expectedCount; + voidPromiseQueue.Enqueue(func.Invoke(resolvePromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseTToT(intReturnProvider)) + { + if (adoptLocation == AdoptLocation.Reject) continue; - resolveDeferred.Resolve(1); - rejectDeferred.Reject("Fail value"); + ++expectedCount; + intPromiseQueue.Enqueue(func.Invoke(resolvePromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseTToVoid(voidReturnProvider)) + { + if (adoptLocation == AdoptLocation.Resolve) continue; - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); + ++expectedCount; + voidPromiseQueue.Enqueue(func.Invoke(rejectPromiseRetainer.WaitAsync())); + } + foreach (var (func, adoptLocation) in TestHelper.GetFunctionsAdoptingPromiseTToT(intReturnProvider)) + { + if (adoptLocation == AdoptLocation.Resolve) continue; + + ++expectedCount; + intPromiseQueue.Enqueue(func.Invoke(rejectPromiseRetainer.WaitAsync())); + } + + resolveDeferred.Resolve(42); + rejectDeferred.Reject("Fail value"); + + Assert.AreEqual(expectedCount, exceptionCounter); + } + } - resolvePromise.Forget(); - rejectPromise.Forget(); + Promise.Config.UncaughtRejectionHandler = currentHandler; } #endif @@ -225,234 +184,262 @@ public void _2_3_2_1_IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRej var resolveDeferred = Promise.NewDeferred(); var rejectDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - var resolveWaitDeferred = Promise.NewDeferred(); - var resolveWaitDeferredInt = Promise.NewDeferred(); - var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitDeferredInt = Promise.NewDeferred(); - - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var resolveWaitPromiseInt = resolveWaitDeferredInt.Promise.Preserve(); - var rejectWaitPromiseInt = rejectWaitDeferredInt.Promise.Preserve(); - - TestAction onCallbackAdded = (ref Promise p) => + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferred.Resolve(); - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: p => + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - rejectDeferred.Reject("Fail outer"); - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - resolveWaitDeferred.Resolve(); - expectedCompleteCount += - (TestHelper.resolveVoidPromiseVoidCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - resolveWaitDeferredInt.Resolve(1); - expectedCompleteCount += - (TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseConvertCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - rejectWaitDeferred.Reject("Fail inner"); - expectedCompleteCount += - (TestHelper.resolveVoidPromiseVoidCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); + var resolveWaitDeferred = Promise.NewDeferred(); + var resolveWaitDeferredInt = Promise.NewDeferred(); + var rejectWaitDeferred = Promise.NewDeferred(); + var rejectWaitDeferredInt = Promise.NewDeferred(); - - rejectWaitDeferredInt.Reject("Fail inner"); - expectedCompleteCount += - (TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseConvertCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - resolvePromise.Forget(); - rejectPromise.Forget(); - resolveWaitPromise.Forget(); - rejectWaitPromise.Forget(); - resolveWaitPromiseInt.Forget(); - rejectWaitPromiseInt.Forget(); + using (var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer()) + { + using (var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer()) + { + using (var resolveWaitPromiseIntRetainer = resolveWaitDeferredInt.Promise.GetRetainer()) + { + using (var rejectWaitPromiseIntRetainer = rejectWaitDeferredInt.Promise.GetRetainer()) + { + bool adopted = false; + TestAction onAdoptCallbackAdded = (ref Promise p) => adopted = true; + TestAction> onAdoptCallbackAddedConvert = (ref Promise p) => adopted = true; + TestAction onCallbackAdded = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + resolveDeferred.Resolve(); + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedCatch: onAdoptCallbackAdded + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedCatch: onAdoptCallbackAdded + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + rejectDeferred.Reject("Fail outer"); + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + resolveWaitDeferred.Resolve(); + expectedCompleteCount += + (TestHelper.resolveVoidPromiseVoidCallbacks + + TestHelper.rejectVoidPromiseVoidCallbacks + + (TestHelper.continueVoidPromiseVoidCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + resolveWaitDeferredInt.Resolve(1); + expectedCompleteCount += + (TestHelper.resolveVoidPromiseConvertCallbacks + + TestHelper.rejectVoidPromiseConvertCallbacks + + (TestHelper.continueVoidPromiseConvertCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + rejectWaitDeferred.Reject("Fail inner"); + expectedCompleteCount += + (TestHelper.resolveVoidPromiseVoidCallbacks + + TestHelper.rejectVoidPromiseVoidCallbacks + + (TestHelper.continueVoidPromiseVoidCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + rejectWaitDeferredInt.Reject("Fail inner"); + expectedCompleteCount += + (TestHelper.resolveVoidPromiseConvertCallbacks + + TestHelper.rejectVoidPromiseConvertCallbacks + + (TestHelper.continueVoidPromiseConvertCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + } + } + } + } + } + } } [Test] @@ -464,251 +451,279 @@ public void _2_3_2_1_IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRej var resolveDeferredInt = Promise.NewDeferred(); var rejectDeferredInt = Promise.NewDeferred(); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - var resolveWaitDeferred = Promise.NewDeferred(); - var resolveWaitDeferredInt = Promise.NewDeferred(); - var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitDeferredInt = Promise.NewDeferred(); - - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); - var resolveWaitPromiseInt = resolveWaitDeferredInt.Promise.Preserve(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var rejectWaitPromiseInt = rejectWaitDeferredInt.Promise.Preserve(); - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - promiseToPromiseT: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferredInt.Resolve(1); - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - promiseToPromiseT: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Forget(); - return resolveWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - promiseToPromiseT: p => + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromise; - }, - promiseToPromiseConvert: p => - { - p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); - return rejectWaitPromiseInt; - }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - rejectDeferredInt.Reject("Fail outer"); - Assert.AreEqual(expectedCompleteCount, completeCounter); + var resolveWaitDeferred = Promise.NewDeferred(); + var resolveWaitDeferredInt = Promise.NewDeferred(); + var rejectWaitDeferred = Promise.NewDeferred(); + var rejectWaitDeferredInt = Promise.NewDeferred(); - - resolveWaitDeferred.Resolve(); - expectedCompleteCount += - (TestHelper.resolveTPromiseVoidCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + - (TestHelper.continueTPromiseVoidCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - resolveWaitDeferredInt.Resolve(1); - expectedCompleteCount += - (TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseConvertCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - rejectWaitDeferred.Reject("Fail inner"); - expectedCompleteCount += - (TestHelper.resolveTPromiseVoidCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + - (TestHelper.continueTPromiseVoidCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - - rejectWaitDeferredInt.Reject("Fail inner"); - expectedCompleteCount += - (TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseConvertCallbacks * 2)) * 2; - Assert.AreEqual(expectedCompleteCount, completeCounter); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - resolveWaitPromise.Forget(); - resolveWaitPromiseInt.Forget(); - rejectWaitPromise.Forget(); - rejectWaitPromiseInt.Forget(); + using (var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer()) + { + using (var resolveWaitPromiseIntRetainer = resolveWaitDeferredInt.Promise.GetRetainer()) + { + using (var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer()) + { + using (var rejectWaitPromiseIntRetainer = rejectWaitDeferredInt.Promise.GetRetainer()) + { + bool adopted = false; + TestAction onAdoptCallbackAdded = (ref Promise p) => adopted = true; + TestAction> onAdoptCallbackAddedConvert = (ref Promise p) => adopted = true; + TestAction onCallbackAdded = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + promiseToPromiseT: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + resolveDeferredInt.Resolve(1); + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + promiseToPromiseT: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedT: onAdoptCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Forget(); + return resolveWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + promiseToPromiseT: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedT: onAdoptCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + promiseToPromise: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseRetainer.WaitAsync(); + }, + promiseToPromiseConvert: p => + { + p.Finally(() => ++completeCounter).Catch(() => { }).Forget(); + return rejectWaitPromiseIntRetainer.WaitAsync(); + }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + rejectDeferredInt.Reject("Fail outer"); + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + resolveWaitDeferred.Resolve(); + expectedCompleteCount += + (TestHelper.resolveTPromiseVoidCallbacks + + TestHelper.rejectTPromiseVoidCallbacks + + (TestHelper.continueTPromiseVoidCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + resolveWaitDeferredInt.Resolve(1); + expectedCompleteCount += + (TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + (TestHelper.continueTPromiseConvertCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + rejectWaitDeferred.Reject("Fail inner"); + expectedCompleteCount += + (TestHelper.resolveTPromiseVoidCallbacks + + TestHelper.rejectTPromiseVoidCallbacks + + (TestHelper.continueTPromiseVoidCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + + + rejectWaitDeferredInt.Reject("Fail inner"); + expectedCompleteCount += + (TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + (TestHelper.continueTPromiseConvertCallbacks * 2)) * 2; + Assert.AreEqual(expectedCompleteCount, completeCounter); + } + } + } + } + } + } } [Test] @@ -724,117 +739,118 @@ public void _2_3_2_2_IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue() resolveDeferredInt.Resolve(1); rejectDeferredInt.Reject("Fail value"); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - int resolveValue = 100; - int resolveCounter = 0; - - TestAction> onCallbackAddedConvert = (ref Promise p) => + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - p.Then(v => + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - if (resolveValue == v) + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) { - ++resolveCounter; + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) + { + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + + int resolveValue = 100; + int resolveCounter = 0; + + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + p.Then(v => + { + if (resolveValue == v) + { + ++resolveCounter; + } + }).Forget(); + }; + + var resolveWaitDeferredInt = Promise.NewDeferred(); + using (var resolveWaitPromiseIntRetainer = resolveWaitDeferredInt.Promise.GetRetainer()) + { + Func, Promise> promiseToPromiseConvert = p => resolveWaitPromiseIntRetainer.WaitAsync(); + + // Test pending -> resolved and already resolved. + bool firstRun = true; + RunAgain: + resolveCounter = 0; + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + promiseToPromiseConvert: promiseToPromiseConvert, + promiseToPromiseT: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + + if (firstRun) + { + Assert.AreEqual(0, resolveCounter); + resolveWaitDeferredInt.Resolve(resolveValue); + } + + Assert.AreEqual( + (TestHelper.resolveVoidPromiseConvertCallbacks + TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectVoidPromiseConvertCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + ((TestHelper.continueVoidPromiseConvertCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, + resolveCounter + ); + + if (firstRun) + { + firstRun = false; + goto RunAgain; + } + } + } } - }).Forget(); - }; - - var resolveWaitDeferredInt = Promise.NewDeferred(); - var resolveWaitPromiseInt = resolveWaitDeferredInt.Promise.Preserve(); - - Func, Promise> promiseToPromiseConvert = p => resolveWaitPromiseInt; - - // Test pending -> resolved and already resolved. - bool firstRun = true; - RunAgain: - resolveCounter = 0; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromiseConvert: promiseToPromiseConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - - if (firstRun) - { - Assert.AreEqual(0, resolveCounter); - resolveWaitDeferredInt.Resolve(resolveValue); - } - - Assert.AreEqual( - (TestHelper.resolveVoidPromiseConvertCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseConvertCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - ((TestHelper.continueVoidPromiseConvertCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, - resolveCounter - ); - - if (firstRun) - { - firstRun = false; - goto RunAgain; + } } - - resolvePromise.Forget(); - rejectPromise.Forget(); - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - - resolveWaitPromiseInt.Forget(); } [Test] @@ -846,111 +862,111 @@ public void _2_3_2_3_IfWhenXIsRejected_RejectPromiseWithTheSameReason_void() resolveDeferred.Resolve(); rejectDeferred.Reject("Fail value"); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - string rejectValue = "Waited Rejection"; - int rejectCounter = 0; - - TestAction onCallbackAdded = (ref Promise p) => + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - p.Catch((string rej) => + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - if (rejectValue == rej) + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + + string rejectValue = "Waited Rejection"; + int rejectCounter = 0; + + TestAction onCallbackAdded = (ref Promise p) => { - ++rejectCounter; - } - }).Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - p.Catch((string rej) => - { - if (rejectValue == rej) + p.Catch((string rej) => + { + if (rejectValue == rej) + { + ++rejectCounter; + } + }).Forget(); + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + p.Catch((string rej) => + { + if (rejectValue == rej) + { + ++rejectCounter; + } + }).Forget(); + }; + + var rejectWaitDeferred = Promise.NewDeferred(); + var rejectWaitDeferredInt = Promise.NewDeferred(); + + using (var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer()) { - ++rejectCounter; + using (var rejectWaitPromiseIntRetainer = rejectWaitDeferredInt.Promise.GetRetainer()) + { + Func promiseToPromise = p => rejectWaitPromiseRetainer.WaitAsync(); + Func, Promise> promiseToPromiseConvert = p => rejectWaitPromiseIntRetainer.WaitAsync(); + + // Test pending -> rejected and already rejected. + bool firstRun = true; + RunAgain: + rejectCounter = 0; + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + + if (firstRun) + { + Assert.AreEqual(0, rejectCounter); + rejectWaitDeferred.Reject(rejectValue); + rejectWaitDeferredInt.Reject(rejectValue); + } + + Assert.AreEqual( + (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + + TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + + ((TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2)) * 2, + rejectCounter + ); + + if (firstRun) + { + firstRun = false; + goto RunAgain; + } + } } - }).Forget(); - }; - - var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitDeferredInt = Promise.NewDeferred(); - - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var rejectWaitPromiseInt = rejectWaitDeferredInt.Promise.Preserve(); - - Func promiseToPromise = p => rejectWaitPromise; - Func, Promise> promiseToPromiseConvert = p => rejectWaitPromiseInt; - - // Test pending -> rejected and already rejected. - bool firstRun = true; - RunAgain: - rejectCounter = 0; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - - if (firstRun) - { - Assert.AreEqual(0, rejectCounter); - rejectWaitDeferred.Reject(rejectValue); - rejectWaitDeferredInt.Reject(rejectValue); - } - - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - ((TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2)) * 2, - rejectCounter - ); - - if (firstRun) - { - firstRun = false; - goto RunAgain; + } } - - resolvePromise.Forget(); - rejectPromise.Forget(); - - rejectWaitPromise.Forget(); - rejectWaitPromiseInt.Forget(); } [Test] @@ -962,116 +978,115 @@ public void _2_3_2_3_IfWhenXIsRejected_RejectPromiseWithTheSameReason_T() resolveDeferredInt.Resolve(1); rejectDeferredInt.Reject("Fail value"); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - string rejectValue = "Waited Rejection"; - int rejectCounter = 0; - - TestAction onCallbackAdded = (ref Promise p) => + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) { - p.Catch((string rej) => + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) { - if (rejectValue == rej) + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + + string rejectValue = "Waited Rejection"; + int rejectCounter = 0; + + TestAction onCallbackAdded = (ref Promise p) => { - ++rejectCounter; - } - }).Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - p.Catch((string rej) => - { - if (rejectValue == rej) + p.Catch((string rej) => + { + if (rejectValue == rej) + { + ++rejectCounter; + } + }).Forget(); + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + p.Catch((string rej) => + { + if (rejectValue == rej) + { + ++rejectCounter; + } + }).Forget(); + }; + + var rejectWaitDeferred = Promise.NewDeferred(); + var rejectWaitDeferredInt = Promise.NewDeferred(); + + using (var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer()) { - ++rejectCounter; + using (var rejectWaitPromiseIntRetainer = rejectWaitDeferredInt.Promise.GetRetainer()) + { + Func promiseToPromise = p => rejectWaitPromiseRetainer.WaitAsync(); + Func, Promise> promiseToPromiseConvert = p => rejectWaitPromiseIntRetainer.WaitAsync(); + + // Test pending -> rejected and already rejected. + bool firstRun = true; + RunAgain: + rejectCounter = 0; + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert, + promiseToPromiseT: promiseToPromiseConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert, + promiseToPromiseT: promiseToPromiseConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + onCallbackAdded: onCallbackAdded, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + + if (firstRun) + { + Assert.AreEqual(0, rejectCounter); + rejectWaitDeferred.Reject(rejectValue); + rejectWaitDeferredInt.Reject(rejectValue); + } + + Assert.AreEqual( + (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + ((TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, + rejectCounter + ); + + if (firstRun) + { + firstRun = false; + goto RunAgain; + } + } } - }).Forget(); - }; - - var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitDeferredInt = Promise.NewDeferred(); - - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var rejectWaitPromiseInt = rejectWaitDeferredInt.Promise.Preserve(); - - - Func promiseToPromise = p => rejectWaitPromise; - Func, Promise> promiseToPromiseConvert = p => rejectWaitPromiseInt; - - // Test pending -> rejected and already rejected. - bool firstRun = true; - RunAgain: - rejectCounter = 0; - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: promiseToPromise, - onCallbackAdded: onCallbackAdded, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - - if (firstRun) - { - Assert.AreEqual(0, rejectCounter); - rejectWaitDeferred.Reject(rejectValue); - rejectWaitDeferredInt.Reject(rejectValue); - } - - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - ((TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, - rejectCounter - ); - - if (firstRun) - { - firstRun = false; - goto RunAgain; + } } - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - - rejectWaitPromise.Forget(); - rejectWaitPromiseInt.Forget(); } } @@ -1084,55 +1099,55 @@ public void _2_3_4_IfOnResolvedOrOnRejectedReturnsSuccessfully_ResolvePromise_vo var resolveDeferred = Promise.NewDeferred(); var rejectDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - int resolveCounter = 0; - - TestAction onCallbackAdded = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); - - TestHelper.AddResolveCallbacks(resolvePromise, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveOnlyVoidCallbacks + TestHelper.resolveVoidCallbacks + - TestHelper.rejectVoidCallbacks + - (TestHelper.continueVoidCallbacks * 2)) * 2, - resolveCounter - ); - - resolvePromise.Forget(); - rejectPromise.Forget(); - } + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) + { + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) + { + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + + int resolveCounter = 0; + + TestAction onCallbackAdded = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + resolveDeferred.Resolve(); + rejectDeferred.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveOnlyVoidCallbacks + TestHelper.resolveVoidCallbacks + + TestHelper.rejectVoidCallbacks + + (TestHelper.continueVoidCallbacks * 2)) * 2, + resolveCounter + ); + } + } + } [Test] public void _2_3_4_IfOnResolvedOrOnRejectedReturnsSuccessfully_ResolvePromise_T() @@ -1140,56 +1155,56 @@ public void _2_3_4_IfOnResolvedOrOnRejectedReturnsSuccessfully_ResolvePromise_T( var resolveDeferredInt = Promise.NewDeferred(); var rejectDeferredInt = Promise.NewDeferred(); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - int resolveCounter = 0; - - TestAction onCallbackAdded = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveOnlyTCallbacks + TestHelper.resolveTCallbacks + - TestHelper.rejectTCallbacks + - (TestHelper.continueTCallbacks * 2)) * 2, - resolveCounter - ); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) + { + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) + { + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + + int resolveCounter = 0; + + TestAction onCallbackAdded = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => p.Then(() => ++resolveCounter).Forget(); + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + resolveDeferredInt.Resolve(1); + rejectDeferredInt.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveOnlyTCallbacks + TestHelper.resolveTCallbacks + + TestHelper.rejectTCallbacks + + (TestHelper.continueTCallbacks * 2)) * 2, + resolveCounter + ); + } + } } [Test] @@ -1198,60 +1213,60 @@ public void _2_3_4_IfXIsNotAPromiseOrAFunction_FulfillPromiseWithX_void() var resolveDeferred = Promise.NewDeferred(); var rejectDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - int expected = 100; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - int resolveCounter = 0; - - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Then(v => + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) + { + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) { - Assert.AreEqual(expected, v); - ++resolveCounter; - }).Forget(); + int expected = 100; - TestHelper.AddResolveCallbacks(resolvePromise, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - TestHelper.AddContinueCallbacks(resolvePromise, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - TestHelper.AddContinueCallbacks(rejectPromise, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); + int resolveCounter = 0; - Assert.AreEqual( - (TestHelper.resolveVoidConvertCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidConvertCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - ((TestHelper.continueVoidConvertCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2)) * 2, - resolveCounter - ); - - resolvePromise.Forget(); - rejectPromise.Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Then(v => + { + Assert.AreEqual(expected, v); + ++resolveCounter; + }).Forget(); + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + + resolveDeferred.Resolve(); + rejectDeferred.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveVoidConvertCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + + TestHelper.rejectVoidConvertCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + + ((TestHelper.continueVoidConvertCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2)) * 2, + resolveCounter + ); + } + } } [Test] @@ -1260,62 +1275,62 @@ public void _2_3_4_IfXIsNotAPromiseOrAFunction_FulfillPromiseWithX_T() var resolveDeferredInt = Promise.NewDeferred(); var rejectDeferredInt = Promise.NewDeferred(); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - int expected = 100; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - - int resolveCounter = 0; - - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Then(v => + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) + { + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) { - Assert.AreEqual(expected, v); - ++resolveCounter; - }).Forget(); - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert, - convertValue: expected, - TValue: expected - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - onCallbackAddedConvert: onCallbackAddedConvert, - convertValue: expected - ); + int expected = 100; - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Assert.AreEqual( - (TestHelper.resolveTConvertCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTConvertCallbacks + TestHelper.rejectTTCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - ((TestHelper.continueTConvertCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, - resolveCounter - ); + int resolveCounter = 0; - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Then(v => + { + Assert.AreEqual(expected, v); + ++resolveCounter; + }).Forget(); + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert, + convertValue: expected, + TValue: expected + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onCallbackAddedConvert: onCallbackAddedConvert, + convertValue: expected + ); + + resolveDeferredInt.Resolve(1); + rejectDeferredInt.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveTConvertCallbacks + TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectTConvertCallbacks + TestHelper.rejectTTCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + ((TestHelper.continueTConvertCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2)) * 2, + resolveCounter + ); + } + } } // If a promise is resolved with a thenable that participates in a circular thenable chain, such that the recursive @@ -1324,823 +1339,380 @@ public void _2_3_4_IfXIsNotAPromiseOrAFunction_FulfillPromiseWithX_T() // not required, to detect such recursion and reject promise with an informative Exception as the reason. #if PROMISE_DEBUG - [Test] - public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_void() + private static void TestCircularAwait_void(Func circularPromiseVoidGetter, Func.Retainer, Promise> circularPromiseTGetter) { + string expectedMessage = "Circular Promise chain detected."; var resolveDeferred = Promise.NewDeferred(); var rejectDeferred = Promise.NewDeferred(); - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => + using (var resolvePromiseRetainer = resolveDeferred.Promise.GetRetainer()) { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return promise.ThenDuplicate().ThenDuplicate().Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return promise.ThenDuplicate().ThenDuplicate().Catch(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); + using (var rejectPromiseRetainer = rejectDeferred.Promise.GetRetainer()) + { + int exceptionCounter = 0; - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + Action catcher = (object o) => + { + Assert.IsInstanceOf(o); + Assert.AreEqual(expectedMessage, o.UnsafeAs().Message); + ++exceptionCounter; + }; - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); + Func promiseToPromise = promise => + { + using (var promiseRetainer = promise.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return circularPromiseVoidGetter(promiseRetainer); + } + }; - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); + Func, Promise> promiseToPromiseConvert = promise => + { + using (var promiseRetainer = promise.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return circularPromiseTGetter(promiseRetainer); + } + }; - resolvePromise.Forget(); - rejectPromise.Forget(); + bool adopted = false; + TestAction onAdoptCallbackAdded = (ref Promise p) => adopted = true; + TestAction> onAdoptCallbackAddedConvert = (ref Promise p) => adopted = true; + TestAction onCallbackAdded = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + + TestHelper.AddResolveCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseRetainer.WaitAsync(), + onResolve: resolveAssert, + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedCatch: onAdoptCallbackAdded + ); + TestHelper.AddContinueCallbacks(rejectPromiseRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + resolveDeferred.Resolve(); + rejectDeferred.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + + TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + + (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, + exceptionCounter + ); + } + } } - [Test] - public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_T() + private static void TestCircularAwait_T(Func circularPromiseVoidGetter, Func.Retainer, Promise> circularPromiseTGetter) { + string expectedMessage = "Circular Promise chain detected."; var resolveDeferredInt = Promise.NewDeferred(); var rejectDeferredInt = Promise.NewDeferred(); - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => + using (var resolvePromiseIntRetainer = resolveDeferredInt.Promise.GetRetainer()) { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; + using (var rejectPromiseIntRetainer = rejectDeferredInt.Promise.GetRetainer()) + { + int exceptionCounter = 0; - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return promise.ThenDuplicate().ThenDuplicate().Catch(() => { }); - }; + Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); + Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); + Action catcher = (object o) => + { + Assert.IsInstanceOf(o); + Assert.AreEqual(expectedMessage, o.UnsafeAs().Message); + ++exceptionCounter; + }; - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return promise.ThenDuplicate().ThenDuplicate().Catch(() => 1); - }; + Func promiseToPromise = promise => + { + using (var promiseRetainer = promise.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return circularPromiseVoidGetter(promiseRetainer); + } + }; - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; + Func, Promise> promiseToPromiseConvert = promise => + { + using (var promiseRetainer = promise.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return circularPromiseTGetter(promiseRetainer); + } + }; - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); + bool adopted = false; + TestAction onAdoptCallbackAdded = (ref Promise p) => adopted = true; + TestAction> onAdoptCallbackAddedConvert = (ref Promise p) => adopted = true; + TestAction onCallbackAdded = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + if (!adopted) + { + p.Forget(); + } + adopted = false; + }; + + TestHelper.AddResolveCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + TestHelper.AddCallbacks(resolvePromiseIntRetainer.WaitAsync(), + onReject: _ => rejectAssert(), + onUnknownRejection: rejectAssert, + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Reject + ); + TestHelper.AddContinueCallbacks(resolvePromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + TestHelper.AddCallbacks(rejectPromiseIntRetainer.WaitAsync(), + onResolve: _ => resolveAssert(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + promiseToPromiseT: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert, + onAdoptCallbackAdded: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedConvert: (ref Promise p, AdoptLocation adoptLocation) => adopted = adoptLocation != AdoptLocation.Resolve, + onAdoptCallbackAddedT: onAdoptCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(rejectPromiseIntRetainer.WaitAsync(), + promiseToPromise: promiseToPromise, + promiseToPromiseConvert: promiseToPromiseConvert, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onAdoptCallbackAdded: onAdoptCallbackAdded, + onAdoptCallbackAddedConvert: onAdoptCallbackAddedConvert + ); + + resolveDeferredInt.Resolve(1); + rejectDeferredInt.Reject("Fail value"); + + Assert.AreEqual( + (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + + TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + + (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, + exceptionCounter + ); + } + } + } - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert + [Test] + public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_void() + { + TestCircularAwait_void( + promiseRetainer => promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => { }), + promiseRetainer => promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => 1) ); + } - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, - exceptionCounter + [Test] + public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_T() + { + TestCircularAwait_T( + promiseRetainer => promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => { }), + promiseRetainer => promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => 1) ); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_race_void() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferred = Promise.NewDeferred(); - var rejectDeferred = Promise.NewDeferred(); - - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => - { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.Race(extraPromise, promise.ThenDuplicate().ThenDuplicate()).Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - promise.Catch(catcher).Forget(); - return Promise.Race(extraPromise, promise.ThenDuplicate().ThenDuplicate()).Catch(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromise.Forget(); - rejectPromise.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_void( + promiseRetainer => Promise.Race( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.Race( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => 1) + ); + extraDeferred.Resolve(1); + } } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_race_T() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferredInt = Promise.NewDeferred(); - var rejectDeferredInt = Promise.NewDeferred(); - - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => - { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.Race(promise.ThenDuplicate().ThenDuplicate(), extraPromise).Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return Promise.Race(promise.ThenDuplicate().ThenDuplicate(), extraPromise).Catch(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_T( + promiseRetainer => Promise.Race( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.Race( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => 1) + ); + extraDeferred.Resolve(1); + } } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_first_void() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferred = Promise.NewDeferred(); - var rejectDeferred = Promise.NewDeferred(); - - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => - { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.First(promise.ThenDuplicate().ThenDuplicate(), extraPromise).Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return Promise.First(promise.ThenDuplicate().ThenDuplicate(), extraPromise).Catch(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromise.Forget(); - rejectPromise.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_void( + promiseRetainer => Promise.First( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.First( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => 1) + ); + extraDeferred.Resolve(1); + } } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_first_T() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferredInt = Promise.NewDeferred(); - var rejectDeferredInt = Promise.NewDeferred(); - - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => - { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.First(extraPromise, promise.ThenDuplicate().ThenDuplicate()).Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return Promise.First(extraPromise, promise.ThenDuplicate().ThenDuplicate()).Catch(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_T( + promiseRetainer => Promise.First( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.First( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => 1) + ); + extraDeferred.Resolve(1); + } } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_all_void() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferred = Promise.NewDeferred(); - var rejectDeferred = Promise.NewDeferred(); - - var resolvePromise = resolveDeferred.Promise.Preserve(); - var rejectPromise = rejectDeferred.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => - { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.All(promise.ThenDuplicate().ThenDuplicate(), extraPromise).Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return Promise.All(promise.ThenDuplicate().ThenDuplicate(), extraPromise) - .Catch(() => { }) - .Then(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromise, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromise, - onResolve: resolveAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromise, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferred.Resolve(); - rejectDeferred.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveVoidPromiseVoidCallbacks + TestHelper.resolveVoidPromiseConvertCallbacks + - TestHelper.rejectVoidPromiseVoidCallbacks + TestHelper.rejectVoidPromiseConvertCallbacks + - (TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromise.Forget(); - rejectPromise.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_void( + promiseRetainer => Promise.All( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.All( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }).Then(() => 1) + ); + extraDeferred.Resolve(1); + } } [Test] public void _2_3_5_IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_all_T() { var extraDeferred = Promise.NewDeferred(); - var extraPromise = extraDeferred.Promise.Preserve(); - - var resolveDeferredInt = Promise.NewDeferred(); - var rejectDeferredInt = Promise.NewDeferred(); - - var resolvePromiseInt = resolveDeferredInt.Promise.Preserve(); - var rejectPromiseInt = rejectDeferredInt.Promise.Preserve(); - - int exceptionCounter = 0; - - Action rejectAssert = () => Assert.Fail("Promise was rejected when it should have been resolved."); - Action resolveAssert = () => Assert.Fail("Promise was resolved when it should have been rejected."); - Action catcher = (object o) => + using (var extraPromiseRetainer = extraDeferred.Promise.GetRetainer()) { - Assert.IsInstanceOf(o); - ++exceptionCounter; - }; - - Func promiseToPromise = promise => - { - promise.Catch(catcher).Forget(); - return Promise.All(extraPromise, promise.ThenDuplicate().ThenDuplicate()) - .Catch(() => { }); - }; - - Func, Promise> promiseToPromiseConvert = promise => - { - promise.Catch(catcher).Forget(); - return Promise.All(extraPromise, promise.ThenDuplicate().ThenDuplicate()) - .Catch(() => { }) - .Then(() => 1); - }; - - TestAction onCallbackAdded = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - TestAction> onCallbackAddedConvert = (ref Promise p) => - { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); - }; - - TestHelper.AddResolveCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(resolvePromiseInt, - onReject: _ => rejectAssert(), - onUnknownRejection: rejectAssert, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(resolvePromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - TestHelper.AddCallbacks(rejectPromiseInt, - onResolve: _ => resolveAssert(), - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - promiseToPromiseT: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(rejectPromiseInt, - promiseToPromise: promiseToPromise, - promiseToPromiseConvert: promiseToPromiseConvert, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - resolveDeferredInt.Resolve(1); - rejectDeferredInt.Reject("Fail value"); - - Assert.AreEqual( - (TestHelper.resolveTPromiseVoidCallbacks + TestHelper.resolveTPromiseConvertCallbacks + - TestHelper.rejectTPromiseVoidCallbacks + TestHelper.rejectTPromiseConvertCallbacks + TestHelper.rejectTPromiseTCallbacks + - (TestHelper.continueTPromiseVoidCallbacks + TestHelper.continueTPromiseConvertCallbacks) * 2) * 2, - exceptionCounter - ); - - resolvePromiseInt.Forget(); - rejectPromiseInt.Forget(); - extraDeferred.Resolve(1); - extraPromise.Forget(); + TestCircularAwait_T( + promiseRetainer => Promise.All( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }), + promiseRetainer => Promise.All( + promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate(), + extraPromiseRetainer.WaitAsync() + ).Catch(() => { }).Then(() => 1) + ); + extraDeferred.Resolve(1); + } } #endif } diff --git a/Package/Tests/CoreTests/APIs/AllSettledTests.cs b/Package/Tests/CoreTests/APIs/AllSettledTests.cs index 46a5de7a..c3dccc13 100644 --- a/Package/Tests/CoreTests/APIs/AllSettledTests.cs +++ b/Package/Tests/CoreTests/APIs/AllSettledTests.cs @@ -336,39 +336,39 @@ public void AllSettledPromiseIsPendingWhenAnyPromiseIsAlreadyRejected_void() string reason = "reject"; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Rejected(reason).Preserve(); - - Promise.AllSettled(promise1, promise2) - .Finally(() => ++invokeCount) - .Then(results => - { - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Resolved, results[0].State); - Assert.AreEqual(Promise.State.Rejected, results[1].State); - Assert.AreEqual(reason, results[1].Reason); - }) - .Forget(); - - Promise.AllSettled(promise2, promise1) - .Finally(() => ++invokeCount) - .Then(results => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Rejected(reason).GetRetainer()) { - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Rejected, results[0].State); - Assert.AreEqual(Promise.State.Resolved, results[1].State); - Assert.AreEqual(reason, results[0].Reason); - }) - .Forget(); - - Assert.AreEqual(0, invokeCount); - - deferred.Resolve(); - - Assert.AreEqual(2, invokeCount); - - promise1.Forget(); - promise2.Forget(); + Promise.AllSettled(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Finally(() => ++invokeCount) + .Then(results => + { + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Resolved, results[0].State); + Assert.AreEqual(Promise.State.Rejected, results[1].State); + Assert.AreEqual(reason, results[1].Reason); + }) + .Forget(); + + Promise.AllSettled(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Finally(() => ++invokeCount) + .Then(results => + { + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Rejected, results[0].State); + Assert.AreEqual(Promise.State.Resolved, results[1].State); + Assert.AreEqual(reason, results[0].Reason); + }) + .Forget(); + + Assert.AreEqual(0, invokeCount); + + deferred.Resolve(); + + Assert.AreEqual(2, invokeCount); + } + } } [Test] @@ -378,41 +378,41 @@ public void AllSettledPromiseIsPendingWhenAnyPromiseIsAlreadyRejected_T() string reason = "reject"; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Rejected(reason).Preserve(); - - Promise.AllSettled(promise1, promise2) - .Finally(() => ++invokeCount) - .Then(results => - { - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Resolved, results[0].State); - Assert.AreEqual(Promise.State.Rejected, results[1].State); - Assert.AreEqual(1, results[0].Value); - Assert.AreEqual(reason, results[1].Reason); - }) - .Forget(); - - Promise.AllSettled(promise2, promise1) - .Finally(() => ++invokeCount) - .Then(results => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Rejected(reason).GetRetainer()) { - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Rejected, results[0].State); - Assert.AreEqual(Promise.State.Resolved, results[1].State); - Assert.AreEqual(1, results[1].Value); - Assert.AreEqual(reason, results[0].Reason); - }) - .Forget(); - - Assert.AreEqual(0, invokeCount); - - deferred.Resolve(1); - - Assert.AreEqual(2, invokeCount); - - promise1.Forget(); - promise2.Forget(); + Promise.AllSettled(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Finally(() => ++invokeCount) + .Then(results => + { + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Resolved, results[0].State); + Assert.AreEqual(Promise.State.Rejected, results[1].State); + Assert.AreEqual(1, results[0].Value); + Assert.AreEqual(reason, results[1].Reason); + }) + .Forget(); + + Promise.AllSettled(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Finally(() => ++invokeCount) + .Then(results => + { + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Rejected, results[0].State); + Assert.AreEqual(Promise.State.Resolved, results[1].State); + Assert.AreEqual(1, results[1].Value); + Assert.AreEqual(reason, results[0].Reason); + }) + .Forget(); + + Assert.AreEqual(0, invokeCount); + + deferred.Resolve(1); + + Assert.AreEqual(2, invokeCount); + } + } } [Test] @@ -619,37 +619,37 @@ public void AllSettledPromiseIsPendingWhenAnyPromiseIsAlreadyCanceled_void() int invokeCount = 0; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Canceled().Preserve(); - - Promise.AllSettled(promise1, promise2) - .Then(results => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Canceled().GetRetainer()) { - ++invokeCount; - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Resolved, results[0].State); - Assert.AreEqual(Promise.State.Canceled, results[1].State); - }) - .Forget(); - - Promise.AllSettled(promise2, promise1) - .Then(results => - { - ++invokeCount; - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Canceled, results[0].State); - Assert.AreEqual(Promise.State.Resolved, results[1].State); - }) - .Forget(); - - Assert.AreEqual(0, invokeCount); - - deferred.Resolve(); - - Assert.AreEqual(2, invokeCount); - - promise1.Forget(); - promise2.Forget(); + Promise.AllSettled(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then(results => + { + ++invokeCount; + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Resolved, results[0].State); + Assert.AreEqual(Promise.State.Canceled, results[1].State); + }) + .Forget(); + + Promise.AllSettled(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then(results => + { + ++invokeCount; + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Canceled, results[0].State); + Assert.AreEqual(Promise.State.Resolved, results[1].State); + }) + .Forget(); + + Assert.AreEqual(0, invokeCount); + + deferred.Resolve(); + + Assert.AreEqual(2, invokeCount); + } + } } [Test] @@ -658,39 +658,39 @@ public void AllSettledPromiseIsPendingWhenAnyPromiseIsAlreadyCanceled_T() int invokeCount = 0; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Canceled().Preserve(); - - Promise.AllSettled(promise1, promise2) - .Then(results => - { - ++invokeCount; - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Resolved, results[0].State); - Assert.AreEqual(Promise.State.Canceled, results[1].State); - Assert.AreEqual(1, results[0].Value); - }) - .Forget(); - - Promise.AllSettled(promise2, promise1) - .Then(results => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Canceled().GetRetainer()) { - ++invokeCount; - Assert.AreEqual(2, results.Count); - Assert.AreEqual(Promise.State.Canceled, results[0].State); - Assert.AreEqual(Promise.State.Resolved, results[1].State); - Assert.AreEqual(1, results[1].Value); - }) - .Forget(); - - Assert.AreEqual(0, invokeCount); - - deferred.Resolve(1); - - Assert.AreEqual(2, invokeCount); - - promise1.Forget(); - promise2.Forget(); + Promise.AllSettled(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then(results => + { + ++invokeCount; + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Resolved, results[0].State); + Assert.AreEqual(Promise.State.Canceled, results[1].State); + Assert.AreEqual(1, results[0].Value); + }) + .Forget(); + + Promise.AllSettled(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then(results => + { + ++invokeCount; + Assert.AreEqual(2, results.Count); + Assert.AreEqual(Promise.State.Canceled, results[0].State); + Assert.AreEqual(Promise.State.Resolved, results[1].State); + Assert.AreEqual(1, results[1].Value); + }) + .Forget(); + + Assert.AreEqual(0, invokeCount); + + deferred.Resolve(1); + + Assert.AreEqual(2, invokeCount); + } + } } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/AllTests.cs b/Package/Tests/CoreTests/APIs/AllTests.cs index aefe1e25..74f7c28e 100644 --- a/Package/Tests/CoreTests/APIs/AllTests.cs +++ b/Package/Tests/CoreTests/APIs/AllTests.cs @@ -222,65 +222,71 @@ public void AllPromiseIsRejectedWhenSecondPromiseIsRejected_T() [Test] public void AllPromiseIsRejectedWhenBothPromisesAreRejected_void() { + // All does not suppress rejections if one of the promises is rejected before the others complete. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + bool uncaughtHandled = false; + Promise.Config.UncaughtRejectionHandler = e => + { + uncaughtHandled = true; + Assert.AreEqual("Error 2", e.Value); + }; + var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var promise1 = deferred1.Promise.Preserve(); - var promise2 = deferred2.Promise.Preserve(); - - // All does not suppress rejections if one of the promises is rejected before the others complete. - promise1.Catch((string _) => { }).Forget(); - promise2.Catch((string _) => { }).Forget(); bool errored = false; - Promise.All(promise1, promise2) + Promise.All(deferred1.Promise, deferred2.Promise) .Then( () => Assert.Fail("Promise was resolved when it should have been rejected."), (string e) => { errored = true; }) .Forget(); - deferred1.Reject("Error!"); + deferred1.Reject("Error 1"); Assert.IsTrue(errored); - deferred2.Reject("Error!"); + deferred2.Reject("Error 2"); Assert.IsTrue(errored); - promise1.Forget(); - promise2.Forget(); + Assert.True(uncaughtHandled); + Promise.Config.UncaughtRejectionHandler = currentHandler; } [Test] public void AllPromiseIsRejectedWhenBothPromisesAreRejected_T() { + // All does not suppress rejections if one of the promises is rejected before the others complete. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + bool uncaughtHandled = false; + Promise.Config.UncaughtRejectionHandler = e => + { + uncaughtHandled = true; + Assert.AreEqual("Error 2", e.Value); + }; + var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var promise1 = deferred1.Promise.Preserve(); - var promise2 = deferred2.Promise.Preserve(); - - // All does not suppress rejections if one of the promises is rejected before the others complete. - promise1.Catch((string _) => { }).Forget(); - promise2.Catch((string _) => { }).Forget(); bool errored = false; - Promise.All(promise1, promise2) + Promise.All(deferred1.Promise, deferred2.Promise) .Then( v => Assert.Fail("Promise was resolved when it should have been rejected."), (string e) => { errored = true; }) .Forget(); - deferred1.Reject("Error!"); + deferred1.Reject("Error 1"); Assert.IsTrue(errored); - deferred2.Reject("Error!"); + deferred2.Reject("Error 2"); Assert.IsTrue(errored); - promise1.Forget(); - promise2.Forget(); + Assert.True(uncaughtHandled); + Promise.Config.UncaughtRejectionHandler = currentHandler; } [Test] @@ -290,37 +296,37 @@ public void AllPromiseIsRejectedWhenAnyPromiseIsAlreadyRejected_void() string rejection = "Error!"; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Rejected(rejection).Preserve(); - - Promise.All(promise1, promise2) - .Then( - () => Assert.Fail("Promise was resolved when it should have been rejected."), - (string ex) => - { - Assert.AreEqual(rejection, ex); - ++rejectCount; - }) - .Forget(); - - Promise.All(promise2, promise1) - .Then( - () => Assert.Fail("Promise was resolved when it should have been rejected."), - (string ex) => - { - Assert.AreEqual(rejection, ex); - ++rejectCount; - }) - .Forget(); - - Assert.AreEqual(2, rejectCount); - - deferred.Resolve(); - - Assert.AreEqual(2, rejectCount); - - promise1.Forget(); - promise2.Forget(); + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Rejected(rejection).GetRetainer()) + { + Promise.All(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then( + () => Assert.Fail("Promise was resolved when it should have been rejected."), + (string ex) => + { + Assert.AreEqual(rejection, ex); + ++rejectCount; + }) + .Forget(); + + Promise.All(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then( + () => Assert.Fail("Promise was resolved when it should have been rejected."), + (string ex) => + { + Assert.AreEqual(rejection, ex); + ++rejectCount; + }) + .Forget(); + + Assert.AreEqual(2, rejectCount); + + deferred.Resolve(); + + Assert.AreEqual(2, rejectCount); + } + } } [Test] @@ -330,37 +336,37 @@ public void AllPromiseIsRejectedWhenAnyPromiseIsAlreadyRejected_T() string rejection = "Error!"; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Rejected(rejection).Preserve(); - - Promise.All(promise1, promise2) - .Then( - v => Assert.Fail("Promise was resolved when it should have been rejected."), - (string ex) => - { - Assert.AreEqual(rejection, ex); - ++rejectCount; - }) - .Forget(); - - Promise.All(promise2, promise1) - .Then( - v => Assert.Fail("Promise was resolved when it should have been rejected."), - (string ex) => - { - Assert.AreEqual(rejection, ex); - ++rejectCount; - }) - .Forget(); - - Assert.AreEqual(2, rejectCount); - - deferred.Resolve(1); - - Assert.AreEqual(2, rejectCount); - - promise1.Forget(); - promise2.Forget(); + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Rejected(rejection).GetRetainer()) + { + Promise.All(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then( + v => Assert.Fail("Promise was resolved when it should have been rejected."), + (string ex) => + { + Assert.AreEqual(rejection, ex); + ++rejectCount; + }) + .Forget(); + + Promise.All(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then( + v => Assert.Fail("Promise was resolved when it should have been rejected."), + (string ex) => + { + Assert.AreEqual(rejection, ex); + ++rejectCount; + }) + .Forget(); + + Assert.AreEqual(2, rejectCount); + + deferred.Resolve(1); + + Assert.AreEqual(2, rejectCount); + } + } } [Test] @@ -535,31 +541,31 @@ public void AllPromiseIsCancelededWhenAnyPromiseIsAlreadyCanceled_void() int cancelCount = 0; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Canceled().Preserve(); - - Promise.All(promise1, promise2) - .Then(() => Assert.Fail("Promise was resolved when it should have been canceled.")) - .CatchCancelation(() => - { - ++cancelCount; - }) - .Forget(); - - Promise.All(promise2, promise1) - .Then(() => Assert.Fail("Promise was resolved when it should have been canceled.")) - .CatchCancelation(() => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Canceled().GetRetainer()) { - ++cancelCount; - }) - .Forget(); - - deferred.Resolve(); - - Assert.AreEqual(2, cancelCount); - - promise1.Forget(); - promise2.Forget(); + Promise.All(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then(() => Assert.Fail("Promise was resolved when it should have been canceled.")) + .CatchCancelation(() => + { + ++cancelCount; + }) + .Forget(); + + Promise.All(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then(() => Assert.Fail("Promise was resolved when it should have been canceled.")) + .CatchCancelation(() => + { + ++cancelCount; + }) + .Forget(); + + deferred.Resolve(); + + Assert.AreEqual(2, cancelCount); + } + } } [Test] @@ -568,31 +574,31 @@ public void AllPromiseIsCancelededWhenAnyPromiseIsAlreadyCanceled_T() int cancelCount = 0; var deferred = Promise.NewDeferred(); - var promise1 = deferred.Promise.Preserve(); - var promise2 = Promise.Canceled().Preserve(); - - Promise.All(promise1, promise2) - .Then(v => Assert.Fail("Promise was resolved when it should have been canceled.")) - .CatchCancelation(() => - { - ++cancelCount; - }) - .Forget(); - - Promise.All(promise2, promise1) - .Then(v => Assert.Fail("Promise was resolved when it should have been canceled.")) - .CatchCancelation(() => + using (var promiseRetainer1 = deferred.Promise.GetRetainer()) + { + using (var promiseRetainer2 = Promise.Canceled().GetRetainer()) { - ++cancelCount; - }) - .Forget(); - - deferred.Resolve(1); - - Assert.AreEqual(2, cancelCount); - - promise1.Forget(); - promise2.Forget(); + Promise.All(promiseRetainer1.WaitAsync(), promiseRetainer2.WaitAsync()) + .Then(v => Assert.Fail("Promise was resolved when it should have been canceled.")) + .CatchCancelation(() => + { + ++cancelCount; + }) + .Forget(); + + Promise.All(promiseRetainer2.WaitAsync(), promiseRetainer1.WaitAsync()) + .Then(v => Assert.Fail("Promise was resolved when it should have been canceled.")) + .CatchCancelation(() => + { + ++cancelCount; + }) + .Forget(); + + deferred.Resolve(1); + + Assert.AreEqual(2, cancelCount); + } + } } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/AsyncFunctionTests.cs b/Package/Tests/CoreTests/APIs/AsyncFunctionTests.cs index 7094fe58..b05cc5ee 100644 --- a/Package/Tests/CoreTests/APIs/AsyncFunctionTests.cs +++ b/Package/Tests/CoreTests/APIs/AsyncFunctionTests.cs @@ -269,7 +269,6 @@ public void AsyncPromiseIsCanceledFromPromise1() var deferred = Promise.NewDeferred(); cancelationSource.Token.Register(deferred); - //System.Diagnostics.Debugger.Launch(); bool canceled = false; async Promise Func() diff --git a/Package/Tests/CoreTests/APIs/AwaitTests.cs b/Package/Tests/CoreTests/APIs/AwaitTests.cs index 59e8e785..eabcbc20 100644 --- a/Package/Tests/CoreTests/APIs/AwaitTests.cs +++ b/Package/Tests/CoreTests/APIs/AwaitTests.cs @@ -565,335 +565,333 @@ async void Func() } [Test] - public void ResolveDoubleAwaitedPromiseContinuesExecution() + public void ResolveDoubleAwaitedRetainedPromiseContinuesExecution() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - await promise; - ++continuedCount; - } + int continuedCount = 0; - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); + async void Func() + { + await promiseRetainer; + ++continuedCount; + } - deferred.Resolve(); - Assert.AreEqual(2, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); + + deferred.Resolve(); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void ResolveDoubleAwaitedPromiseReturnsValueAndContinuesExecution() + public void ResolveDoubleAwaitedRetainedPromiseReturnsValueAndContinuesExecution() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - int expected = 50; - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int value = await promise; - Assert.AreEqual(expected, value); - ++continuedCount; - } + int expected = 50; + int continuedCount = 0; - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); + async void Func() + { + int value = await promiseRetainer; + Assert.AreEqual(expected, value); + ++continuedCount; + } - deferred.Resolve(expected); - Assert.AreEqual(2, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); + + deferred.Resolve(expected); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyResolvedPromiseContinuesExecution() + public void DoubleAwaitAlreadyResolvedRetainedPromiseContinuesExecution() { - var promise = Promise.Resolved().Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Resolved().GetRetainer()) { - await promise; - ++continuedCount; - } + int continuedCount = 0; - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + async void Func() + { + await promiseRetainer; + ++continuedCount; + } + + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyResolvedPromiseReturnsValueAndContinuesExecution() + public void DoubleAwaitAlreadyResolvedRetainedPromiseReturnsValueAndContinuesExecution() { int expected = 50; - var promise = Promise.Resolved(expected).Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Resolved(expected).GetRetainer()) { - int value = await promise; - Assert.AreEqual(expected, value); - ++continuedCount; - } + int continuedCount = 0; - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + async void Func() + { + int value = await promiseRetainer; + Assert.AreEqual(expected, value); + ++continuedCount; + } - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); + + Assert.AreEqual(2, continuedCount); + } } [Test] - public void RejectDoubleAwaitedPromiseThrows_void() + public void RejectDoubleAwaitedRetainedPromiseThrows_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string rejectValue = "Reject"; - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - try - { - await promise; - } - catch (UnhandledException e) + string rejectValue = "Reject"; + int continuedCount = 0; + + async void Func() { - Assert.AreEqual(rejectValue, e.Value); - ++continuedCount; + try + { + await promiseRetainer; + } + catch (UnhandledException e) + { + Assert.AreEqual(rejectValue, e.Value); + ++continuedCount; + } } - } - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); - deferred.Reject(rejectValue); - Assert.AreEqual(2, continuedCount); + deferred.Reject(rejectValue); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void RejectDoubleAwaitedPromiseThrows_T() + public void RejectDoubleAwaitedRetainedPromiseThrows_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string rejectValue = "Reject"; - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - try - { - int value = await promise; - } - catch (UnhandledException e) + string rejectValue = "Reject"; + int continuedCount = 0; + + async void Func() { - Assert.AreEqual(rejectValue, e.Value); - ++continuedCount; + try + { + int value = await promiseRetainer; + } + catch (UnhandledException e) + { + Assert.AreEqual(rejectValue, e.Value); + ++continuedCount; + } } - } - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); - deferred.Reject(rejectValue); - Assert.AreEqual(2, continuedCount); + deferred.Reject(rejectValue); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyRejectedPromiseThrows_void() + public void DoubleAwaitAlreadyRejectedRetainedPromiseThrows_void() { string rejectValue = "Reject"; - var promise = Promise.Rejected(rejectValue).Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Rejected(rejectValue).GetRetainer()) { - try - { - await promise; - } - catch (UnhandledException e) + int continuedCount = 0; + + async void Func() { - Assert.AreEqual(rejectValue, e.Value); - ++continuedCount; + try + { + await promiseRetainer; + } + catch (UnhandledException e) + { + Assert.AreEqual(rejectValue, e.Value); + ++continuedCount; + } } - } - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyRejectedPromiseThrows_T() + public void DoubleAwaitAlreadyRejectedRetainedPromiseThrows_T() { string rejectValue = "Reject"; - var promise = Promise.Rejected(rejectValue).Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Rejected(rejectValue).GetRetainer()) { - try - { - int value = await promise; - } - catch (UnhandledException e) + int continuedCount = 0; + + async void Func() { - Assert.AreEqual(rejectValue, e.Value); - ++continuedCount; + try + { + int value = await promiseRetainer; + } + catch (UnhandledException e) + { + Assert.AreEqual(rejectValue, e.Value); + ++continuedCount; + } } - } - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void CancelDoubleAwaitedPromiseThrowsOperationCanceled_void() + public void CancelDoubleAwaitedRetainedPromiseThrowsOperationCanceled_void() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - try - { - await promise; - } - catch (CanceledException) + int continuedCount = 0; + + async void Func() { - ++continuedCount; + try + { + await promiseRetainer; + } + catch (CanceledException) + { + ++continuedCount; + } } - } - - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); - cancelationSource.Cancel(); - Assert.AreEqual(2, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); - cancelationSource.Dispose(); + deferred.Cancel(); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void CancelDoubleAwaitedPromiseThrowsOperationCanceled_T() + public void CancelDoubleAwaitedRetainedPromiseThrowsOperationCanceled_T() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - try - { - int value = await promise; - } - catch (CanceledException) + int continuedCount = 0; + + async void Func() { - ++continuedCount; + try + { + int value = await promiseRetainer; + } + catch (CanceledException) + { + ++continuedCount; + } } - } - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(0, continuedCount); - cancelationSource.Cancel(); - Assert.AreEqual(2, continuedCount); - - cancelationSource.Dispose(); + deferred.Cancel(); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyCanceledPromiseThrowsOperationCanceled_void() + public void DoubleAwaitAlreadyCanceledRetainedPromiseThrowsOperationCanceled_void() { - var promise = Promise.Canceled().Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Canceled().GetRetainer()) { - try - { - await promise; - } - catch (CanceledException) + int continuedCount = 0; + + async void Func() { - ++continuedCount; + try + { + await promiseRetainer; + } + catch (CanceledException) + { + ++continuedCount; + } } - } - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(2, continuedCount); + } } [Test] - public void DoubleAwaitAlreadyCanceledPromiseThrowsOperationCanceled_T() + public void DoubleAwaitAlreadyCanceledRetainedPromiseThrowsOperationCanceled_T() { - var promise = Promise.Canceled().Preserve(); - int continuedCount = 0; - - async void Func() + using (var promiseRetainer = Promise.Canceled().GetRetainer()) { - try - { - int value = await promise; - } - catch (CanceledException) + int continuedCount = 0; + + async void Func() { - ++continuedCount; + try + { + int value = await promiseRetainer; + } + catch (CanceledException) + { + ++continuedCount; + } } - } - Assert.AreEqual(0, continuedCount); - Func(); - Func(); - promise.Forget(); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(0, continuedCount); + Func(); + Func(); + Assert.AreEqual(2, continuedCount); - Assert.AreEqual(2, continuedCount); + Assert.AreEqual(2, continuedCount); + } } [Test] diff --git a/Package/Tests/CoreTests/APIs/CancelationTests.cs b/Package/Tests/CoreTests/APIs/CancelationTests.cs index f4bbe27a..39280242 100644 --- a/Package/Tests/CoreTests/APIs/CancelationTests.cs +++ b/Package/Tests/CoreTests/APIs/CancelationTests.cs @@ -1710,7 +1710,7 @@ public void CancelationRegistrationDisposeAsyncDoesNotCompleteUntilTheCallbackIs CancelationToken cancelationToken = cancelationSource.Token; var promise = Promise.Run(RunAsync, SynchronizationOption.Background, forceAsync: true); - SpinWait.SpinUntil(() => isCallbackRegistered); + TestHelper.SpinUntil(() => isCallbackRegistered, TimeSpan.FromSeconds(1)); var cancelPromise = Promise.Run(cancelationSource.Cancel, SynchronizationOption.Background, forceAsync: true); Thread.Sleep(100); @@ -1730,11 +1730,11 @@ async Promise RunAsync() var registration = cancelationToken.Register(1, cv => { continueAsyncFunction = true; - SpinWait.SpinUntil(() => isCallbackComplete); + TestHelper.SpinUntil(() => isCallbackComplete, TimeSpan.FromSeconds(1)); }); { isCallbackRegistered = true; - SpinWait.SpinUntil(() => continueAsyncFunction); + TestHelper.SpinUntil(() => continueAsyncFunction, TimeSpan.FromSeconds(1)); } await registration.DisposeAsync(); completedAsync = true; @@ -1751,7 +1751,7 @@ public void CancelationRegistrationAwaitUsingDoesNotCompleteUntilTheCallbackIsCo CancelationToken cancelationToken = cancelationSource.Token; var promise = Promise.Run(RunAsync, SynchronizationOption.Background, forceAsync: true); - SpinWait.SpinUntil(() => isCallbackRegistered); + TestHelper.SpinUntil(() => isCallbackRegistered, TimeSpan.FromSeconds(1)); var cancelPromise = Promise.Run(cancelationSource.Cancel, SynchronizationOption.Background, forceAsync: true); Thread.Sleep(100); @@ -1771,11 +1771,11 @@ async Promise RunAsync() await using (cancelationToken.Register(1, cv => { continueAsyncFunction = true; - SpinWait.SpinUntil(() => isCallbackComplete); + TestHelper.SpinUntil(() => isCallbackComplete, TimeSpan.FromSeconds(1)); })) { isCallbackRegistered = true; - SpinWait.SpinUntil(() => continueAsyncFunction); + TestHelper.SpinUntil(() => continueAsyncFunction, TimeSpan.FromSeconds(1)); } completedAsync = true; } @@ -1799,7 +1799,7 @@ public void CancelationRegistrationDisposeWaitsForCallbackToComplete_0() invoked = true; }); Promise.Run(() => cancelationSource.Cancel()).Forget(); - SpinWait.SpinUntil(() => startedInvoke); + TestHelper.SpinUntil(() => startedInvoke, TimeSpan.FromSeconds(1)); cancelationRegistration.Dispose(); Assert.IsTrue(invoked); cancelationSource.Dispose(); @@ -1820,7 +1820,7 @@ public void CancelationRegistrationDisposeWaitsForCallbackToComplete_1() })) { Promise.Run(() => cancelationSource.Cancel()).Forget(); - SpinWait.SpinUntil(() => startedInvoke); + TestHelper.SpinUntil(() => startedInvoke, TimeSpan.FromSeconds(1)); } Assert.IsTrue(invoked); cancelationSource.Dispose(); @@ -1846,7 +1846,7 @@ async Promise RunAsync() invoked = true; }); Promise.Run(() => cancelationSource.Cancel()).Forget(); - SpinWait.SpinUntil(() => startedInvoke); + TestHelper.SpinUntil(() => startedInvoke, TimeSpan.FromSeconds(1)); await cancelationRegistration.DisposeAsync(); Assert.IsTrue(invoked); asyncComplete = true; @@ -1876,7 +1876,7 @@ async Promise RunAsync() })) { Promise.Run(() => cancelationSource.Cancel()).Forget(); - SpinWait.SpinUntil(() => startedInvoke); + TestHelper.SpinUntil(() => startedInvoke, TimeSpan.FromSeconds(1)); } Assert.IsTrue(invoked); asyncComplete = true; diff --git a/Package/Tests/CoreTests/APIs/CaptureTests.cs b/Package/Tests/CoreTests/APIs/CaptureTests.cs index 6a74a8ae..a3765b1c 100644 --- a/Package/Tests/CoreTests/APIs/CaptureTests.cs +++ b/Package/Tests/CoreTests/APIs/CaptureTests.cs @@ -31,12 +31,10 @@ public void Teardown() public void IfOnCanceledIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.CatchCancelation(100, default(Action)); - }); + Assert.Throws(() => promise.CatchCancelation(100, default(Action))); + Assert.Throws(() => promise.CatchCancelation(100, default(Func))); deferred.Resolve(); promise.Forget(); @@ -46,12 +44,10 @@ public void IfOnCanceledIsNullThrow_void() public void IfOnCanceledIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.CatchCancelation(100, default(Action)); - }); + Assert.Throws(() => promise.CatchCancelation(100, default(Action))); + Assert.Throws(() => promise.CatchCancelation(100, default(Func))); deferred.Resolve(1); promise.Forget(); @@ -61,15 +57,12 @@ public void IfOnCanceledIsNullThrow_T() public void IfOnFinallyIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Finally(100, default(Action)); - }); + Assert.Throws(() => promise.Finally(100, default(Action))); + Assert.Throws(() => promise.Finally(100, default(Func))); deferred.Resolve(); - promise.Forget(); } @@ -77,15 +70,12 @@ public void IfOnFinallyIsNullThrow_void() public void IfOnFinallyIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Finally(100, default(Action)); - }); + Assert.Throws(() => promise.Finally(100, default(Action))); + Assert.Throws(() => promise.Finally(100, default(Func))); deferred.Resolve(1); - promise.Forget(); } @@ -93,27 +83,14 @@ public void IfOnFinallyIsNullThrow_T() public void IfOnContinueIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.ContinueWith(100, default(Action)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func>)); - }); + Assert.Throws(() => promise.ContinueWith(100, default(Action))); + Assert.Throws(() => promise.ContinueWith(100, default(Func))); + Assert.Throws(() => promise.ContinueWith(100, default(Func))); + Assert.Throws(() => promise.ContinueWith(100, default(Func>))); deferred.Resolve(); - promise.Forget(); } @@ -121,27 +98,14 @@ public void IfOnContinueIsNullThrow_void() public void IfOnContinueIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.ContinueWith(100, default(Action.ResultContainer>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func.ResultContainer, bool>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func.ResultContainer, Promise>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(100, default(Func.ResultContainer, Promise>)); - }); + Assert.Throws(() => promise.ContinueWith(100, default(Action.ResultContainer>))); + Assert.Throws(() => promise.ContinueWith(100, default(Func.ResultContainer, bool>))); + Assert.Throws(() => promise.ContinueWith(100, default(Func.ResultContainer, Promise>))); + Assert.Throws(() => promise.ContinueWith(100, default(Func.ResultContainer, Promise>))); deferred.Resolve(1); - promise.Forget(); } @@ -149,98 +113,36 @@ public void IfOnContinueIsNullThrow_T() public void IfOnFulfilledIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Then(100, default(Action)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func>)); - }); + Assert.Throws(() => promise.Then(100, default(Action))); + Assert.Throws(() => promise.Then(100, default(Func))); + Assert.Throws(() => promise.Then(100, default(Func))); + Assert.Throws(() => promise.Then(100, default(Func>))); + Assert.Throws(() => promise.Then(100, default(Action), () => { })); + Assert.Throws(() => promise.Then(100, default(Action), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(100, default(Action), () => { }); - }); - Assert.Throws(() => - { - promise.Then(100, default(Action), (string failValue) => { }); - }); + Assert.Throws(() => promise.Then(100, default(Func), () => default(bool))); + Assert.Throws(() => promise.Then(100, default(Func), (string failValue) => default(bool))); - Assert.Throws(() => - { - promise.Then(100, default(Func), () => default(bool)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func), (string failValue) => default(bool)); - }); + Assert.Throws(() => promise.Then(100, default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(100, default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(100, default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(100, default(Func>), () => default(Promise))); + Assert.Throws(() => promise.Then(100, default(Func>), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(100, default(Func>), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func>), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(100, default(Action), () => default(Promise))); + Assert.Throws(() => promise.Then(100, default(Action), (string failValue) => default(Promise))); + Assert.Throws(() => promise.Then(100, default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(100, default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(100, default(Action), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Action), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(100, default(Func), () => { })); + Assert.Throws(() => promise.Then(100, default(Func), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(100, default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func), (string failValue) => default(Promise)); - }); - - Assert.Throws(() => - { - promise.Then(100, default(Func), () => { }); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func), (string failValue) => { }); - }); - - Assert.Throws(() => - { - promise.Then(100, default(Func>), () => default(bool)); - }); - Assert.Throws(() => - { - promise.Then(100, default(Func>), (string failValue) => default(bool)); - }); + Assert.Throws(() => promise.Then(100, default(Func>), () => default(bool))); + Assert.Throws(() => promise.Then(100, default(Func>), (string failValue) => default(bool))); deferred.Resolve(); promise.Forget(); @@ -250,98 +152,36 @@ public void IfOnFulfilledIsNullThrow_void() public void IfOnFulfilledIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Then(true, default(Action)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func>)); - }); + Assert.Throws(() => promise.Then(true, default(Action))); + Assert.Throws(() => promise.Then(true, default(Func))); + Assert.Throws(() => promise.Then(true, default(Func))); + Assert.Throws(() => promise.Then(true, default(Func>))); + Assert.Throws(() => promise.Then(true, default(Action), () => { })); + Assert.Throws(() => promise.Then(true, default(Action), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(true, default(Action), () => { }); - }); - Assert.Throws(() => - { - promise.Then(true, default(Action), (string failValue) => { }); - }); + Assert.Throws(() => promise.Then(true, default(Func), () => default(int))); + Assert.Throws(() => promise.Then(true, default(Func), (string failValue) => default(int))); - Assert.Throws(() => - { - promise.Then(true, default(Func), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(true, default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(true, default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(true, default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(true, default(Func>), () => default(Promise))); + Assert.Throws(() => promise.Then(true, default(Func>), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(true, default(Func>), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func>), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(true, default(Action), () => default(Promise))); + Assert.Throws(() => promise.Then(true, default(Action), (string failValue) => default(Promise))); + Assert.Throws(() => promise.Then(true, default(Func), () => default(Promise))); + Assert.Throws(() => promise.Then(true, default(Func), (string failValue) => default(Promise))); - Assert.Throws(() => - { - promise.Then(true, default(Action), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Action), (string failValue) => default(Promise)); - }); + Assert.Throws(() => promise.Then(true, default(Func), () => { })); + Assert.Throws(() => promise.Then(true, default(Func), (string failValue) => { })); - Assert.Throws(() => - { - promise.Then(true, default(Func), () => default(Promise)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func), (string failValue) => default(Promise)); - }); - - Assert.Throws(() => - { - promise.Then(true, default(Func), () => { }); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func), (string failValue) => { }); - }); - - Assert.Throws(() => - { - promise.Then(true, default(Func>), () => default(int)); - }); - Assert.Throws(() => - { - promise.Then(true, default(Func>), (string failValue) => default(int)); - }); + Assert.Throws(() => promise.Then(true, default(Func>), () => default(int))); + Assert.Throws(() => promise.Then(true, default(Func>), (string failValue) => default(int))); deferred.Resolve(1); promise.Forget(); @@ -351,99 +191,37 @@ public void IfOnFulfilledIsNullThrow_T() public void IfOnRejectedIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - Assert.Throws(() => - { - promise.Catch(100, default(Action)); - }); - Assert.Throws(() => - { - promise.Catch(100, default(Action)); - }); - - Assert.Throws(() => - { - promise.Catch(100, default(Func)); - }); - Assert.Throws(() => - { - promise.Catch(100, default(Func)); - }); + var promise = deferred.Promise; + Assert.Throws(() => promise.Catch(100, default(Action))); + Assert.Throws(() => promise.Catch(100, default(Action))); - Assert.Throws(() => - { - promise.Then(() => { }, 100, default(Action)); - }); - Assert.Throws(() => - { - promise.Then(() => { }, 100, default(Action)); - }); + Assert.Throws(() => promise.Catch(100, default(Func))); + Assert.Throws(() => promise.Catch(100, default(Func))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func)); - }); + Assert.Throws(() => promise.Then(() => { }, 100, default(Action))); + Assert.Throws(() => promise.Then(() => { }, 100, default(Action))); - Assert.Throws(() => - { - promise.Then(() => "string", 100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => "string", 100, default(Func)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func))); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func>)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func>)); - }); + Assert.Throws(() => promise.Then(() => "string", 100, default(Func))); + Assert.Throws(() => promise.Then(() => "string", 100, default(Func))); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func>))); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func>))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Action)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Action)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Action))); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Action))); - Assert.Throws(() => - { - promise.Then(() => { }, 100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => { }, 100, default(Func)); - }); + Assert.Throws(() => promise.Then(() => { }, 100, default(Func))); + Assert.Throws(() => promise.Then(() => { }, 100, default(Func))); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func)); - }); - Assert.Throws(() => - { - promise.Then(() => default(Promise), 100, default(Func)); - }); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func))); + Assert.Throws(() => promise.Then(() => default(Promise), 100, default(Func))); - Assert.Throws(() => - { - promise.Then(() => "string", 100, default(Func>)); - }); - Assert.Throws(() => - { - promise.Then(() => "string", 100, default(Func>)); - }); + Assert.Throws(() => promise.Then(() => "string", 100, default(Func>))); + Assert.Throws(() => promise.Then(() => "string", 100, default(Func>))); deferred.Resolve(); promise.Forget(); @@ -453,99 +231,37 @@ public void IfOnRejectedIsNullThrow_void() public void IfOnRejectedIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - Assert.Throws(() => - { - promise.Catch(true, default(Func)); - }); - Assert.Throws(() => - { - promise.Catch(true, default(Func)); - }); + var promise = deferred.Promise; - Assert.Throws(() => - { - promise.Catch(true, default(Func>)); - }); - Assert.Throws(() => - { - promise.Catch(true, default(Func>)); - }); + Assert.Throws(() => promise.Catch(true, default(Func))); + Assert.Throws(() => promise.Catch(true, default(Func))); + Assert.Throws(() => promise.Catch(true, default(Func>))); + Assert.Throws(() => promise.Catch(true, default(Func>))); - Assert.Throws(() => - { - promise.Then((int x) => { }, true, default(Action)); - }); - Assert.Throws(() => - { - promise.Then((int x) => { }, true, default(Action)); - }); + Assert.Throws(() => promise.Then((int x) => { }, true, default(Action))); + Assert.Throws(() => promise.Then((int x) => { }, true, default(Action))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func))); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => "string", true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => "string", true, default(Func)); - }); - - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func>)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func>)); - }); + Assert.Throws(() => promise.Then((int x) => "string", true, default(Func))); + Assert.Throws(() => promise.Then((int x) => "string", true, default(Func))); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func>))); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func>))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Action)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Action)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Action))); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Action))); - Assert.Throws(() => - { - promise.Then((int x) => { }, true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => { }, true, default(Func)); - }); + Assert.Throws(() => promise.Then((int x) => { }, true, default(Func))); + Assert.Throws(() => promise.Then((int x) => { }, true, default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func)); - }); - Assert.Throws(() => - { - promise.Then((int x) => default(Promise), true, default(Func)); - }); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func))); + Assert.Throws(() => promise.Then((int x) => default(Promise), true, default(Func))); - Assert.Throws(() => - { - promise.Then((int x) => "string", true, default(Func>)); - }); - Assert.Throws(() => - { - promise.Then((int x) => "string", true, default(Func>)); - }); + Assert.Throws(() => promise.Then((int x) => "string", true, default(Func>))); + Assert.Throws(() => promise.Then((int x) => "string", true, default(Func>))); deferred.Resolve(1); promise.Forget(); @@ -906,12 +622,10 @@ public void PromiseIsRejectedWithThrownExceptionWhenOnFinallyWithCapturedValueTh public void OnContinueWillBeInvokedWithCapturedValue_resolved_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - string expected = "expected"; bool invoked = false; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -923,8 +637,6 @@ public void OnContinueWillBeInvokedWithCapturedValue_resolved_void() deferred.Resolve(); Assert.IsTrue(invoked); - - promise.Forget(); } [Test] @@ -934,9 +646,7 @@ public void OnContinueWillBeInvokedWithCapturedValue_resolved_T() bool invoked = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -948,8 +658,6 @@ public void OnContinueWillBeInvokedWithCapturedValue_resolved_T() deferred.Resolve(50); Assert.IsTrue(invoked); - - promise.Forget(); } [Test] @@ -959,9 +667,7 @@ public void OnContinueWillBeInvokedWithCapturedValue_rejected_void() bool invoked = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -973,8 +679,6 @@ public void OnContinueWillBeInvokedWithCapturedValue_rejected_void() deferred.Reject("Reject"); Assert.IsTrue(invoked); - - promise.Forget(); } [Test] @@ -984,9 +688,7 @@ public void OnContinueWillBeInvokedWithCapturedValue_rejected_T() bool invoked = false; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -998,8 +700,6 @@ public void OnContinueWillBeInvokedWithCapturedValue_rejected_T() deferred.Reject("Reject"); Assert.IsTrue(invoked); - - promise.Forget(); } [Test] @@ -1009,11 +709,9 @@ public void OnContinueWillBeInvokedWithCapturedValue_canceled_void() CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - bool invoked = false; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -1026,7 +724,6 @@ public void OnContinueWillBeInvokedWithCapturedValue_canceled_void() Assert.IsTrue(invoked); cancelationSource.Dispose(); - promise.Forget(); } [Test] @@ -1035,12 +732,10 @@ public void OnContinueWillBeInvokedWithCapturedValue_canceled_T() CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - string expected = "expected"; bool invoked = false; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, captureValue: expected, onContinueCapture: (cv, r) => { @@ -1053,85 +748,80 @@ public void OnContinueWillBeInvokedWithCapturedValue_canceled_T() Assert.IsTrue(invoked); cancelationSource.Dispose(); - promise.Forget(); } [Test] public void OnResolvedWillBeInvokedWithCapturedValue_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string expected = "expected"; - bool invoked = false; - - TestHelper.AddResolveCallbacks(promise, - captureValue: expected, - onResolveCapture: cv => - { - Assert.AreEqual(expected, cv); - invoked = true; - } - ); - TestHelper.AddCallbacks(promise, - captureValue: expected, - onResolveCapture: cv => - { - Assert.AreEqual(expected, cv); - invoked = true; - } - ); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + string expected = "expected"; + bool invoked = false; - deferred.Resolve(); + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + captureValue: expected, + onResolveCapture: cv => + { + Assert.AreEqual(expected, cv); + invoked = true; + } + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + captureValue: expected, + onResolveCapture: cv => + { + Assert.AreEqual(expected, cv); + invoked = true; + } + ); - Assert.IsTrue(invoked); + deferred.Resolve(); - promise.Forget(); + Assert.IsTrue(invoked); + } } [Test] public void OnResolvedWillBeInvokedWithCapturedValue_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - string expected = "expected"; - bool invoked = false; - - TestHelper.AddResolveCallbacks(promise, - captureValue: expected, - onResolveCapture: cv => - { - Assert.AreEqual(expected, cv); - invoked = true; - } - ); - TestHelper.AddCallbacks(promise, - captureValue: expected, - onResolveCapture: cv => - { - Assert.AreEqual(expected, cv); - invoked = true; - } - ); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + string expected = "expected"; + bool invoked = false; - deferred.Resolve(50); + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + captureValue: expected, + onResolveCapture: cv => + { + Assert.AreEqual(expected, cv); + invoked = true; + } + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + captureValue: expected, + onResolveCapture: cv => + { + Assert.AreEqual(expected, cv); + invoked = true; + } + ); - Assert.IsTrue(invoked); + deferred.Resolve(50); - promise.Forget(); + Assert.IsTrue(invoked); + } } [Test] public void OnRejectedWillBeInvokedWithCapturedValue_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - string expected = "expected"; bool invoked = false; - TestHelper.AddCallbacks(promise, + TestHelper.AddCallbacks(deferred.Promise, captureValue: expected, onRejectCapture: cv => { @@ -1148,20 +838,16 @@ public void OnRejectedWillBeInvokedWithCapturedValue_void() deferred.Reject("Reject"); Assert.IsTrue(invoked); - - promise.Forget(); } [Test] public void OnRejectedWillBeInvokedWithCapturedValue_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - string expected = "expected"; bool invoked = false; - TestHelper.AddCallbacks(promise, + TestHelper.AddCallbacks(deferred.Promise, captureValue: expected, onResolveCapture: cv => { @@ -1178,8 +864,6 @@ public void OnRejectedWillBeInvokedWithCapturedValue_T() deferred.Reject("Reject"); Assert.IsTrue(invoked); - - promise.Forget(); } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/ContinuewithTests.cs b/Package/Tests/CoreTests/APIs/ContinuewithTests.cs index 3ca107f3..270a79f7 100644 --- a/Package/Tests/CoreTests/APIs/ContinuewithTests.cs +++ b/Package/Tests/CoreTests/APIs/ContinuewithTests.cs @@ -29,24 +29,12 @@ public void Teardown() public void IfOnContinueIsNullThrow_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - Assert.Throws(() => - { - promise.ContinueWith(default(Action)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func>)); - }); + var promise = deferred.Promise; + + Assert.Throws(() => promise.ContinueWith(default(Action))); + Assert.Throws(() => promise.ContinueWith(default(Func))); + Assert.Throws(() => promise.ContinueWith(default(Func))); + Assert.Throws(() => promise.ContinueWith(default(Func>))); deferred.Resolve(); promise.Forget(); @@ -56,24 +44,12 @@ public void IfOnContinueIsNullThrow_void() public void IfOnContinueIsNullThrow_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - Assert.Throws(() => - { - promise.ContinueWith(default(Action.ResultContainer>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func.ResultContainer, bool>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func.ResultContainer, Promise>)); - }); - Assert.Throws(() => - { - promise.ContinueWith(default(Func.ResultContainer, Promise>)); - }); + var promise = deferred.Promise; + + Assert.Throws(() => promise.ContinueWith(default(Action.ResultContainer>))); + Assert.Throws(() => promise.ContinueWith(default(Func.ResultContainer, bool>))); + Assert.Throws(() => promise.ContinueWith(default(Func.ResultContainer, Promise>))); + Assert.Throws(() => promise.ContinueWith(default(Func.ResultContainer, Promise>))); deferred.Resolve(1); promise.Forget(); @@ -84,64 +60,54 @@ public void IfOnContinueIsNullThrow_T() public void OnContinueIsInvokedWhenPromiseIsResolved_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); deferred.Resolve(); Assert.AreEqual(TestHelper.continueVoidCallbacks * 2, finallyCount); - - promise.Forget(); } [Test] public void OnContinueIsInvokedWhenPromiseIsResolved_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); deferred.Resolve(50); Assert.AreEqual(TestHelper.continueTCallbacks * 2, finallyCount); - - promise.Forget(); } [Test] public void OnContinueStateWhenPromiseIsResolved() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => Assert.AreEqual(r.State, Promise.State.Resolved) ); deferred.Resolve(); - - promise.Forget(); } [Test] public void OnContinueResultWhenPromiseIsResolved() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int expected = 50; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => { Assert.AreEqual(r.State, Promise.State.Resolved); @@ -150,55 +116,46 @@ public void OnContinueResultWhenPromiseIsResolved() ); deferred.Resolve(expected); - - promise.Forget(); } [Test] public void OnContinueIsInvokedWhenPromiseIsRejected_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); deferred.Reject("Reject"); Assert.AreEqual(TestHelper.continueVoidCallbacks * 2, finallyCount); - - promise.Forget(); } [Test] public void OnContinueIsInvokedWhenPromiseIsRejected_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); deferred.Reject("Reject"); Assert.AreEqual(TestHelper.continueTCallbacks * 2, finallyCount); - - promise.Forget(); } [Test] public void OnContinueRejectReasonWhenPromiseIsRejected_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); string rejection = "Reject"; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => { Assert.AreEqual(r.State, Promise.State.Rejected); @@ -207,19 +164,16 @@ public void OnContinueRejectReasonWhenPromiseIsRejected_void() ); deferred.Reject(rejection); - - promise.Forget(); } [Test] public void OnContinueRejectReasonWhenPromiseIsRejected_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); string rejection = "Reject"; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => { Assert.AreEqual(r.State, Promise.State.Rejected); @@ -228,20 +182,17 @@ public void OnContinueRejectReasonWhenPromiseIsRejected_T() ); deferred.Reject(rejection); - - promise.Forget(); } [Test] public void OnContinueRethrowRejectReasonWhenPromiseIsRejected_void() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int rejections = 0; string rejection = "Reject"; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => r.RethrowIfRejected(), onCallbackAdded: (ref Promise p) => p.Catch((object e) => { Assert.AreEqual(rejection, e); ++rejections; }).Forget(), onCallbackAddedConvert: (ref Promise p) => p.Catch((object e) => { Assert.AreEqual(rejection, e); ++rejections; }).Forget() @@ -253,20 +204,17 @@ public void OnContinueRethrowRejectReasonWhenPromiseIsRejected_void() TestHelper.continueVoidCallbacks * 2, rejections ); - - promise.Forget(); } [Test] public void OnContinueRethrowRejectReasonWhenPromiseIsRejected_T() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); int rejections = 0; string rejection = "Reject"; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => r.RethrowIfRejected(), onCallbackAdded: (ref Promise p) => p.Catch((object e) => { Assert.AreEqual(rejection, e); ++rejections; }).Forget(), onCallbackAddedConvert: (ref Promise p) => p.Catch((object e) => { Assert.AreEqual(rejection, e); ++rejections; }).Forget() @@ -278,144 +226,106 @@ public void OnContinueRethrowRejectReasonWhenPromiseIsRejected_T() TestHelper.continueTCallbacks * 2, rejections ); - - promise.Forget(); } [Test] public void OnContinueIsInvokedWhenPromiseIsCanceled_void() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); - cancelationSource.Cancel(); + deferred.Cancel(); Assert.AreEqual(TestHelper.continueVoidCallbacks * 2, finallyCount); - - cancelationSource.Dispose(); - promise.Forget(); } [Test] public void OnContinueIsInvokedWhenPromiseIsCanceled_T() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); int finallyCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => ++finallyCount ); - cancelationSource.Cancel(); + deferred.Cancel(); Assert.AreEqual(TestHelper.continueTCallbacks * 2, finallyCount); - - cancelationSource.Dispose(); - promise.Forget(); } [Test] public void OnContinueCancelStateWhenPromiseIsCanceled_void() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => { Assert.AreEqual(r.State, Promise.State.Canceled); } ); - cancelationSource.Cancel(); - - cancelationSource.Dispose(); - promise.Forget(); + deferred.Cancel(); } [Test] public void OnContinueCancelStateWhenPromiseIsCanceled_T() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => { Assert.AreEqual(r.State, Promise.State.Canceled); } ); - cancelationSource.Cancel(); - - cancelationSource.Dispose(); - promise.Forget(); + deferred.Cancel(); } [Test] public void OnContinueRethrowCancelWhenPromiseIsCanceled_void() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); int cancelCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => r.RethrowIfCanceled(), onCancel: () => { ++cancelCount; } ); - cancelationSource.Cancel(); + deferred.Cancel(); Assert.AreEqual( TestHelper.continueVoidCallbacks * 2, cancelCount ); - - cancelationSource.Dispose(); - promise.Forget(); } [Test] public void OnContinueRethrowCancelReasonWhenPromiseIsCanceled_T() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); int cancelCount = 0; - TestHelper.AddContinueCallbacks(promise, + TestHelper.AddContinueCallbacks(deferred.Promise, onContinue: r => r.RethrowIfCanceled(), onCancel: () => { ++cancelCount; } ); - cancelationSource.Cancel(); + deferred.Cancel(); Assert.AreEqual( TestHelper.continueTCallbacks * 2, cancelCount ); - - cancelationSource.Dispose(); - promise.Forget(); } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/EachTests.cs b/Package/Tests/CoreTests/APIs/EachTests.cs new file mode 100644 index 00000000..69d93688 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/EachTests.cs @@ -0,0 +1,788 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System.Linq; +using Proto.Promises; +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace ProtoPromiseTests.APIs +{ + public class EachTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + public static IEnumerable GetArgs() + { + var completeTypes = new CompleteType[] + { + CompleteType.Resolve, + CompleteType.Reject, + CompleteType.Cancel + }; + var alreadyCompletes = new bool[] { false, true }; + // Only test a few combinations to keep test counts down. + var completeIndicesGroups = new int[][] + { + new int[0], + new int[] { 0, 1, 2 }, + new int[] { 1, 0, 2 }, + new int[] { 2, 0, 1 }, + }; + + var generatedCombos = new List<(CompleteType completeType, bool isAlreadyComplete, int completeIndex)[]>(); + foreach (var completeIndices in completeIndicesGroups) + { + generatedCombos.Clear(); + + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[completeIndices.Length]; + for (int i = 0; i < args.Length; ++i) + { + args[i] = (CompleteType.Resolve, false, completeIndices[i]); + } + generatedCombos.Add(args); + + for (int i = 0; i < completeIndices.Length; ++i) + { + for (int j = 0, max = generatedCombos.Count; j < max; j++) + { + foreach (var completeType in completeTypes) + { + foreach (var alreadyComplete in alreadyCompletes) + { + var combo = generatedCombos[j].ToArray(); + combo[i].completeType = completeType; + combo[i].isAlreadyComplete = alreadyComplete; + if (!generatedCombos.Any(c => Enumerable.SequenceEqual(c, combo))) + { + generatedCombos.Add(combo); + } + } + } + } + } + + foreach (var combo in generatedCombos) + { + // Make sure completeIndex is always correct in the face of already completes. + bool alreadyCompleteOutOfOrder = false; + bool notAlreadyComplete = false; + foreach (var c in combo.OrderBy(x => x.completeIndex)) + { + if (c.isAlreadyComplete && notAlreadyComplete) + { + alreadyCompleteOutOfOrder = true; + break; + } + if (!c.isAlreadyComplete) + { + notAlreadyComplete = true; + } + } + if (alreadyCompleteOutOfOrder) + { + continue; + } + + int lastCompleteIndex = -1; + alreadyCompleteOutOfOrder = false; + foreach (var c in combo) + { + if (!c.isAlreadyComplete) + { + continue; + } + if (c.completeIndex < lastCompleteIndex) + { + alreadyCompleteOutOfOrder = true; + break; + } + lastCompleteIndex = c.completeIndex; + } + if (alreadyCompleteOutOfOrder) + { + continue; + } + + yield return new TestCaseData((object) combo); + } + } + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void EachResultsAreYieldedInCompletionOrder_void((CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] args) + { + Promise.Run(async () => + { + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachResultsAreYieldedInCompletionOrder_MoveNextAsyncBeforeComplete_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, false, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + bool movedNext = false; + var moveNextPromise = asyncEnumerator.MoveNextAsync() + .Finally(() => movedNext = true); + Assert.False(movedNext); + tryCompleters[completeIndex].Invoke(); + Assert.True(await moveNextPromise); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachDisposeEarly_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachDisposeWithoutMoveNext_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + await Promise.Each(promises).GetAsyncEnumerator().DisposeAsync(); + + for (int i = 0; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCancelEarly_void( + [Values] bool disposeCancelationSourceEarly) + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCancelationSourceDisposedEarly_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCanceledAfterAllCompleteAndBeforeMoveNextAsync_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void EachResultsAreYieldedInCompletionOrder_T((CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] args) + { + Promise.Run(async () => + { + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(completeIndex, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachResultsAreYieldedInCompletionOrder_MoveNextAsyncBeforeComplete_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, false, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + bool movedNext = false; + var moveNextPromise = asyncEnumerator.MoveNextAsync() + .Finally(() => movedNext = true); + Assert.False(movedNext); + tryCompleters[completeIndex].Invoke(); + Assert.True(await moveNextPromise); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(completeIndex, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachDisposeEarly_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = Promise.Each(promises).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachDisposeWithoutMoveNext_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + await Promise.Each(promises).GetAsyncEnumerator().DisposeAsync(); + + for (int i = 0; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCancelEarly_T( + [Values] bool disposeCancelationSourceEarly) + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCancelationSourceDisposedEarly_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void EachCanceledAfterAllCompleteAndBeforeMoveNextAsync_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + var promises = new Promise[args.Length]; + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + promises[i] = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = Promise.Each(promises).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/EachTests.cs.meta b/Package/Tests/CoreTests/APIs/EachTests.cs.meta new file mode 100644 index 00000000..12833775 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/EachTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8970d2be6f6ff6b4bb7957056de67bff +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/FinallyTests.cs b/Package/Tests/CoreTests/APIs/FinallyTests.cs index 6e09bd42..9158aeb3 100644 --- a/Package/Tests/CoreTests/APIs/FinallyTests.cs +++ b/Package/Tests/CoreTests/APIs/FinallyTests.cs @@ -29,32 +29,26 @@ public void Teardown() [Test] public void IfOnFinallyIsNullThrow_void() { - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = Promise.Resolved(); Assert.Throws(() => promise.Finally(default(Action))); Assert.Throws(() => promise.Finally(42, default(Action))); Assert.Throws(() => promise.Finally(default(Func))); Assert.Throws(() => promise.Finally(42, default(Func))); - deferred.Resolve(); - promise.Forget(); } [Test] public void IfOnFinallyIsNullThrow_T() { - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promise = Promise.Resolved(1); Assert.Throws(() => promise.Finally(default(Action))); Assert.Throws(() => promise.Finally(42, default(Action))); Assert.Throws(() => promise.Finally(default(Func))); Assert.Throws(() => promise.Finally(42, default(Func))); - deferred.Resolve(1); - promise.Forget(); } #endif @@ -65,55 +59,51 @@ public void OnFinallyIsInvokedWhenPromiseIsSettled_void( [Values] bool isAlreadyComplete) { const string rejection = "Reject"; - Promise.Deferred deferred; - CancelationSource cancelationSource; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int finallyCount = 0; - int completeCount = 0; - const string captureValue = "captureValue"; - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; - promise - .Finally(() => ++finallyCount) - .Finally(captureValue, s => - { - Assert.AreEqual(captureValue, s); - ++finallyCount; - }) - .ContinueWith(resultContainer => - { - if (completeType == CompleteType.Resolve) - { - Assert.AreEqual(Promise.State.Resolved, resultContainer.State); - } - else if (completeType == CompleteType.Reject) + int testCount = 0; + int finallyCount = 0; + int completeCount = 0; + const string captureValue = "captureValue"; + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; + promise + .Finally(() => ++finallyCount) + .Finally(captureValue, s => { - Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection, resultContainer.Reason); - } - else + Assert.AreEqual(captureValue, s); + ++finallyCount; + }) + .ContinueWith(resultContainer => { - Assert.AreEqual(Promise.State.Canceled, resultContainer.State); - } - ++completeCount; - }) - .Forget(); - } - - Assert.Greater(testCount, 0); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, resultContainer.State); + Assert.AreEqual(rejection, resultContainer.Reason); + } + else + { + Assert.AreEqual(Promise.State.Canceled, resultContainer.State); + } + ++completeCount; + }) + .Forget(); + } - TestHelper.GetTryCompleterVoid(completeType, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(2 * testCount, finallyCount); - Assert.AreEqual(testCount, completeCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(2 * testCount, finallyCount); + Assert.AreEqual(testCount, completeCount); + } } [Test] @@ -123,56 +113,52 @@ public void OnFinallyIsInvokedWhenPromiseIsSettled_T( { const int resolveValue = 42; const string rejection = "Reject"; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int finallyCount = 0; - int completeCount = 0; - const string captureValue = "captureValue"; - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; - promise - .Finally(() => ++finallyCount) - .Finally(captureValue, s => - { - Assert.AreEqual(captureValue, s); - ++finallyCount; - }) - .ContinueWith(resultContainer => - { - if (completeType == CompleteType.Resolve) - { - Assert.AreEqual(Promise.State.Resolved, resultContainer.State); - Assert.AreEqual(resolveValue, resultContainer.Value); - } - else if (completeType == CompleteType.Reject) + int testCount = 0; + int finallyCount = 0; + int completeCount = 0; + const string captureValue = "captureValue"; + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; + promise + .Finally(() => ++finallyCount) + .Finally(captureValue, s => { - Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection, resultContainer.Reason); - } - else + Assert.AreEqual(captureValue, s); + ++finallyCount; + }) + .ContinueWith(resultContainer => { - Assert.AreEqual(Promise.State.Canceled, resultContainer.State); - } - ++completeCount; - }) - .Forget(); - } - - Assert.Greater(testCount, 0); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + Assert.AreEqual(resolveValue, resultContainer.Value); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, resultContainer.State); + Assert.AreEqual(rejection, resultContainer.Reason); + } + else + { + Assert.AreEqual(Promise.State.Canceled, resultContainer.State); + } + ++completeCount; + }) + .Forget(); + } - TestHelper.GetTryCompleterT(completeType, resolveValue, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(2 * testCount, finallyCount); - Assert.AreEqual(testCount, completeCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(2 * testCount, finallyCount); + Assert.AreEqual(testCount, completeCount); + } } [Test] @@ -193,42 +179,38 @@ public void PromiseIsRejectedWithThrownExceptionWhenOnFinallyThrows_void( ++uncaughtHandledCount; }; - Promise.Deferred deferred; - CancelationSource cancelationSource; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int catchCount = 0; - Exception expected = new Exception(); - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; + int testCount = 0; + int catchCount = 0; + Exception expected = new Exception(); + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; #pragma warning disable CS0162 // Unreachable code detected - (isAsync - ? promise.Finally(() => { throw expected; return Promise.Resolved(); }) - : promise.Finally(() => { throw expected; })) + (isAsync + ? promise.Finally(() => { throw expected; return Promise.Resolved(); }) + : promise.Finally(() => { throw expected; })) #pragma warning restore CS0162 // Unreachable code detected - .Catch((Exception e) => - { - Assert.AreEqual(expected, e); - ++catchCount; - }) - .Forget(); - } - - Assert.Greater(testCount, 0); + .Catch((Exception e) => + { + Assert.AreEqual(expected, e); + ++catchCount; + }) + .Forget(); + } - TestHelper.GetTryCompleterVoid(completeType, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(testCount, catchCount); - Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(testCount, catchCount); + Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); - Promise.Config.UncaughtRejectionHandler = currentHandler; + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } [Test] @@ -250,42 +232,38 @@ public void PromiseIsRejectedWithThrownExceptionWhenOnFinallyThrows_T( }; const int resolveValue = 42; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int catchCount = 0; - Exception expected = new Exception(); - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; + int testCount = 0; + int catchCount = 0; + Exception expected = new Exception(); + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; #pragma warning disable CS0162 // Unreachable code detected - (isAsync - ? promise.Finally(() => { throw expected; return Promise.Resolved(); }) - : promise.Finally(() => { throw expected; })) + (isAsync + ? promise.Finally(() => { throw expected; return Promise.Resolved(); }) + : promise.Finally(() => { throw expected; })) #pragma warning restore CS0162 // Unreachable code detected - .Catch((Exception e) => - { - Assert.AreEqual(expected, e); - ++catchCount; - }) - .Forget(); - } - - Assert.Greater(testCount, 0); + .Catch((Exception e) => + { + Assert.AreEqual(expected, e); + ++catchCount; + }) + .Forget(); + } - TestHelper.GetTryCompleterT(completeType, resolveValue, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(testCount, catchCount); - Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(testCount, catchCount); + Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); - Promise.Config.UncaughtRejectionHandler = currentHandler; + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } [Test] @@ -306,37 +284,33 @@ public void PromiseIsCanceledWhenOnFinallyThrows_void( ++uncaughtHandledCount; }; - Promise.Deferred deferred; - CancelationSource cancelationSource; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int catchCount = 0; - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; + int testCount = 0; + int catchCount = 0; + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; #pragma warning disable CS0162 // Unreachable code detected - (isAsync - ? promise.Finally(() => { throw Promise.CancelException(); return Promise.Resolved(); }) - : promise.Finally(() => { throw Promise.CancelException(); })) + (isAsync + ? promise.Finally(() => { throw Promise.CancelException(); return Promise.Resolved(); }) + : promise.Finally(() => { throw Promise.CancelException(); })) #pragma warning restore CS0162 // Unreachable code detected - .CatchCancelation(() => ++catchCount) - .Forget(); - } - - Assert.Greater(testCount, 0); + .CatchCancelation(() => ++catchCount) + .Forget(); + } - TestHelper.GetTryCompleterVoid(completeType, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(testCount, catchCount); - Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(testCount, catchCount); + Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); - Promise.Config.UncaughtRejectionHandler = currentHandler; + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } [Test] @@ -358,37 +332,33 @@ public void PromiseIsCanceledWhenOnFinallyThrows_T( }; const int resolveValue = 42; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out deferred, out cancelationSource) - .Preserve(); - - int testCount = 0; - int catchCount = 0; - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection, out var tryCompleter) + .GetRetainer()) { - ++testCount; + int testCount = 0; + int catchCount = 0; + + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++testCount; #pragma warning disable CS0162 // Unreachable code detected - (isAsync - ? promise.Finally(() => { throw Promise.CancelException(); return Promise.Resolved(); }) - : promise.Finally(() => { throw Promise.CancelException(); })) + (isAsync + ? promise.Finally(() => { throw Promise.CancelException(); return Promise.Resolved(); }) + : promise.Finally(() => { throw Promise.CancelException(); })) #pragma warning restore CS0162 // Unreachable code detected - .CatchCancelation(() => ++catchCount) - .Forget(); - } - - Assert.Greater(testCount, 0); + .CatchCancelation(() => ++catchCount) + .Forget(); + } - TestHelper.GetTryCompleterT(completeType, resolveValue, rejection).Invoke(deferred, cancelationSource); + Assert.Greater(testCount, 0); - Assert.AreEqual(testCount, catchCount); - Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); + tryCompleter(); - preservedPromise.Forget(); - cancelationSource.TryDispose(); + Assert.AreEqual(testCount, catchCount); + Assert.AreEqual(completeType == CompleteType.Reject ? testCount : 0, uncaughtHandledCount); - Promise.Config.UncaughtRejectionHandler = currentHandler; + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } [Test] @@ -415,99 +385,91 @@ public void OnFinallyIsInvokedWhenPromiseIsSettled_WaitsForReturnedPromise_void( ++uncaughtHandledCount; }; - Promise.Deferred deferred; - CancelationSource cancelationSource; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection1, out deferred, out cancelationSource) - .Preserve(); + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejection1, out var preservedCompleter) + .GetRetainer()) + { + int finallyCount = 0; + int completeCount = 0; + const string captureValue = "captureValue"; - int finallyCount = 0; - int completeCount = 0; - const string captureValue = "captureValue"; + var returnPromises = new List<(Promise.Retainer retainer, Action tryCompleter)>(); + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + var returnPromiseRetainer = TestHelper.BuildPromise(returnCompleteType, returnIsAlreadyComplete, rejection2, out var tryCompleter) + .GetRetainer(); + returnPromises.Add((returnPromiseRetainer, tryCompleter)); - var returnPromises = new List>(); - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) - { - Promise.Deferred returnDeferred; - CancelationSource returnCts; - var returnPromise = TestHelper.BuildPromise(returnCompleteType, returnIsAlreadyComplete, rejection2, out returnDeferred, out returnCts) - .Preserve(); - returnPromises.Add(ValueTuple.Create(returnDeferred, returnPromise, returnCts)); - - promise - .Finally(() => - { - ++finallyCount; - return returnPromise; - }) - .Finally(captureValue, s => - { - Assert.AreEqual(captureValue, s); - ++finallyCount; - return returnPromise; - }) - .ContinueWith(resultContainer => - { - if (returnCompleteType == CompleteType.Resolve) + promise + .Finally(() => { - if (completeType == CompleteType.Resolve) + ++finallyCount; + return returnPromiseRetainer.WaitAsync(); + }) + .Finally(captureValue, s => + { + Assert.AreEqual(captureValue, s); + ++finallyCount; + return returnPromiseRetainer.WaitAsync(); + }) + .ContinueWith(resultContainer => + { + if (returnCompleteType == CompleteType.Resolve) { - Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, resultContainer.State); + Assert.AreEqual(rejection1, resultContainer.Reason); + } + else + { + Assert.AreEqual(Promise.State.Canceled, resultContainer.State); + } } - else if (completeType == CompleteType.Reject) + else if (returnCompleteType == CompleteType.Reject) { Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection1, resultContainer.Reason); + Assert.AreEqual(rejection2, resultContainer.Reason); } else { Assert.AreEqual(Promise.State.Canceled, resultContainer.State); } - } - else if (returnCompleteType == CompleteType.Reject) - { - Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection2, resultContainer.Reason); - } - else - { - Assert.AreEqual(Promise.State.Canceled, resultContainer.State); - } - ++completeCount; - }) - .Forget(); - } + ++completeCount; + }) + .Forget(); + } - Assert.Greater(returnPromises.Count, 0); + Assert.Greater(returnPromises.Count, 0); - TestHelper.GetTryCompleterVoid(completeType, rejection1).Invoke(deferred, cancelationSource); + preservedCompleter(); - Assert.AreEqual(returnPromises.Count * (returnIsAlreadyComplete ? 2 : 1), finallyCount); - Assert.AreEqual(returnIsAlreadyComplete ? returnPromises.Count : 0, completeCount); + Assert.AreEqual(returnPromises.Count * (returnIsAlreadyComplete ? 2 : 1), finallyCount); + Assert.AreEqual(returnIsAlreadyComplete ? returnPromises.Count : 0, completeCount); - var completer = TestHelper.GetTryCompleterVoid(returnCompleteType, rejection2); - foreach (var tuple in returnPromises) - { - completer.Invoke(tuple.Item1, tuple.Item3); - tuple.Item2.Forget(); - tuple.Item3.TryDispose(); - } + foreach (var (retainer, tryCompleter) in returnPromises) + { + tryCompleter(); + retainer.Dispose(); + } - Assert.AreEqual(returnPromises.Count, completeCount); - int expectedUncaughtCount = completeType == CompleteType.Reject - ? returnCompleteType == CompleteType.Reject - ? returnPromises.Count * 2 - : returnCompleteType != CompleteType.Resolve // Cancel - ? returnPromises.Count - : 0 - : returnCompleteType == CompleteType.Reject - ? returnPromises.Count - : 0; - Assert.AreEqual(expectedUncaughtCount, uncaughtHandledCount); - - preservedPromise.Forget(); - cancelationSource.TryDispose(); - - Promise.Config.UncaughtRejectionHandler = currentHandler; + Assert.AreEqual(returnPromises.Count, completeCount); + int expectedUncaughtCount = completeType == CompleteType.Reject + ? returnCompleteType == CompleteType.Reject + ? returnPromises.Count * 2 + : returnCompleteType != CompleteType.Resolve // Cancel + ? returnPromises.Count + : 0 + : returnCompleteType == CompleteType.Reject + ? returnPromises.Count + : 0; + Assert.AreEqual(expectedUncaughtCount, uncaughtHandledCount); + + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } [Test] @@ -535,99 +497,90 @@ public void OnFinallyIsInvokedWhenPromiseIsSettled_WaitsForReturnedPromise_T( }; const int resolveValue = 42; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var preservedPromise = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection1, out deferred, out cancelationSource) - .Preserve(); - - int finallyCount = 0; - int completeCount = 0; - const string captureValue = "captureValue"; + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, resolveValue, rejection1, out var preservedCompleter) + .GetRetainer()) + { int finallyCount = 0; + int completeCount = 0; + const string captureValue = "captureValue"; + + var returnPromises = new List<(Promise.Retainer retainer, Action tryCompleter)>(); + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + var returnPromise = TestHelper.BuildPromise(returnCompleteType, returnIsAlreadyComplete, rejection2, out var tryCompleter) + .GetRetainer(); + returnPromises.Add((returnPromise, tryCompleter)); - var returnPromises = new List>(); - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) - { - Promise.Deferred returnDeferred; - CancelationSource returnCts; - var returnPromise = TestHelper.BuildPromise(returnCompleteType, returnIsAlreadyComplete, rejection2, out returnDeferred, out returnCts) - .Preserve(); - returnPromises.Add(ValueTuple.Create(returnDeferred, returnPromise, returnCts)); - - promise - .Finally(() => - { - ++finallyCount; - return returnPromise; - }) - .Finally(captureValue, s => - { - Assert.AreEqual(captureValue, s); - ++finallyCount; - return returnPromise; - }) - .ContinueWith(resultContainer => - { - if (returnCompleteType == CompleteType.Resolve) + promise + .Finally(() => { - if (completeType == CompleteType.Resolve) + ++finallyCount; + return returnPromise.WaitAsync(); + }) + .Finally(captureValue, s => + { + Assert.AreEqual(captureValue, s); + ++finallyCount; + return returnPromise.WaitAsync(); + }) + .ContinueWith(resultContainer => + { + if (returnCompleteType == CompleteType.Resolve) { - Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(Promise.State.Resolved, resultContainer.State); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, resultContainer.State); + Assert.AreEqual(rejection1, resultContainer.Reason); + } + else + { + Assert.AreEqual(Promise.State.Canceled, resultContainer.State); + } } - else if (completeType == CompleteType.Reject) + else if (returnCompleteType == CompleteType.Reject) { Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection1, resultContainer.Reason); + Assert.AreEqual(rejection2, resultContainer.Reason); } else { Assert.AreEqual(Promise.State.Canceled, resultContainer.State); } - } - else if (returnCompleteType == CompleteType.Reject) - { - Assert.AreEqual(Promise.State.Rejected, resultContainer.State); - Assert.AreEqual(rejection2, resultContainer.Reason); - } - else - { - Assert.AreEqual(Promise.State.Canceled, resultContainer.State); - } - ++completeCount; - }) - .Forget(); - } + ++completeCount; + }) + .Forget(); + } - Assert.Greater(returnPromises.Count, 0); + Assert.Greater(returnPromises.Count, 0); - TestHelper.GetTryCompleterT(completeType, resolveValue, rejection1).Invoke(deferred, cancelationSource); + preservedCompleter(); - Assert.AreEqual(returnPromises.Count * (returnIsAlreadyComplete ? 2 : 1), finallyCount); - Assert.AreEqual(returnIsAlreadyComplete ? returnPromises.Count : 0, completeCount); + Assert.AreEqual(returnPromises.Count * (returnIsAlreadyComplete ? 2 : 1), finallyCount); + Assert.AreEqual(returnIsAlreadyComplete ? returnPromises.Count : 0, completeCount); - var completer = TestHelper.GetTryCompleterVoid(returnCompleteType, rejection2); - foreach (var tuple in returnPromises) - { - completer.Invoke(tuple.Item1, tuple.Item3); - tuple.Item2.Forget(); - tuple.Item3.TryDispose(); - } + foreach (var (retainer, tryCompleter) in returnPromises) + { + tryCompleter(); + retainer.Dispose(); + } - Assert.AreEqual(returnPromises.Count, completeCount); - int expectedUncaughtCount = completeType == CompleteType.Reject - ? returnCompleteType == CompleteType.Reject - ? returnPromises.Count * 2 - : returnCompleteType != CompleteType.Resolve // Cancel - ? returnPromises.Count - : 0 - : returnCompleteType == CompleteType.Reject - ? returnPromises.Count - : 0; - Assert.AreEqual(expectedUncaughtCount, uncaughtHandledCount); - - preservedPromise.Forget(); - cancelationSource.TryDispose(); - - Promise.Config.UncaughtRejectionHandler = currentHandler; + Assert.AreEqual(returnPromises.Count, completeCount); + int expectedUncaughtCount = completeType == CompleteType.Reject + ? returnCompleteType == CompleteType.Reject + ? returnPromises.Count * 2 + : returnCompleteType != CompleteType.Resolve // Cancel + ? returnPromises.Count + : 0 + : returnCompleteType == CompleteType.Reject + ? returnPromises.Count + : 0; + Assert.AreEqual(expectedUncaughtCount, uncaughtHandledCount); + + Promise.Config.UncaughtRejectionHandler = currentHandler; + } } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/FirstTests.cs b/Package/Tests/CoreTests/APIs/FirstTests.cs index ac41a4ff..746d2401 100644 --- a/Package/Tests/CoreTests/APIs/FirstTests.cs +++ b/Package/Tests/CoreTests/APIs/FirstTests.cs @@ -27,8 +27,8 @@ public void Teardown() public void FirstIsResolvedWhenFirstPromiseIsResolvedFirst_void( [Values] bool alreadyResolved) { - var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred1, out _); - var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter1); + var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -39,11 +39,11 @@ public void FirstIsResolvedWhenFirstPromiseIsResolvedFirst_void( }) .Forget(); - deferred1.TryResolve(); + tryCompleter1(); Assert.IsTrue(resolved); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -52,8 +52,8 @@ public void FirstIsResolvedWhenFirstPromiseIsResolvedFirst_void( public void FirstIsResolvedWhenFirstPromiseIsResolvedFirst_T( [Values] bool alreadyResolved) { - var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred1, out _); - var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 2, "Error", out var deferred2, out _); + var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter1); + var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 2, "Error", out var tryCompleter2); bool resolved = false; @@ -65,11 +65,11 @@ public void FirstIsResolvedWhenFirstPromiseIsResolvedFirst_T( }) .Forget(); - deferred1.TryResolve(5); + tryCompleter1(); Assert.IsTrue(resolved); - deferred2.TryResolve(1); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -79,7 +79,7 @@ public void FirstIsResolvedWhenSecondPromiseIsResolvedFirst_void( [Values] bool alreadyResolved) { var deferred1 = Promise.NewDeferred(); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -90,7 +90,7 @@ public void FirstIsResolvedWhenSecondPromiseIsResolvedFirst_void( }) .Forget(); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); @@ -104,7 +104,7 @@ public void FirstIsResolvedWhenSecondPromiseIsResolvedFirst_T( [Values] bool alreadyResolved) { var deferred1 = Promise.NewDeferred(); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred2, out _); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -116,7 +116,7 @@ public void FirstIsResolvedWhenSecondPromiseIsResolvedFirst_T( }) .Forget(); - deferred2.TryResolve(5); + tryCompleter2(); Assert.IsTrue(resolved); @@ -130,8 +130,8 @@ public void FirstIsResolvedWhenFirstPromiseIsRejectedThenSecondPromiseIsResolved [Values] bool alreadyRejected, [Values] bool alreadyResolved) { - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Error", out var deferred1, out _); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Error", out var tryCompleter1); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -142,11 +142,11 @@ public void FirstIsResolvedWhenFirstPromiseIsRejectedThenSecondPromiseIsResolved }) .Forget(); - deferred1.TryReject("Error"); + tryCompleter1(); Assert.AreEqual(alreadyResolved, resolved); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -156,8 +156,8 @@ public void FirstIsResolvedWhenFirstPromiseIsRejectedThenSecondPromiseIsResolved [Values] bool alreadyRejected, [Values] bool alreadyResolved) { - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var deferred1, out _); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var tryCompleter1); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -169,11 +169,11 @@ public void FirstIsResolvedWhenFirstPromiseIsRejectedThenSecondPromiseIsResolved }) .Forget(); - deferred1.TryReject("Error"); + tryCompleter1(); Assert.AreEqual(alreadyResolved, resolved); - deferred2.TryResolve(5); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -183,7 +183,7 @@ public void FirstIsResolvedWhenSecondPromiseIsRejectedThenFirstPromiseIsResolved [Values] bool alreadyRejected) { var deferred1 = Promise.NewDeferred(); - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Error", out var tryCompleter2); bool resolved = false; @@ -194,7 +194,7 @@ public void FirstIsResolvedWhenSecondPromiseIsRejectedThenFirstPromiseIsResolved }) .Forget(); - deferred2.TryReject("Error"); + tryCompleter2(); Assert.IsFalse(resolved); @@ -208,7 +208,7 @@ public void FirstIsResolvedWhenSecondPromiseIsRejectedThenFirstPromiseIsResolved [Values] bool alreadyRejected) { var deferred1 = Promise.NewDeferred(); - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -220,7 +220,7 @@ public void FirstIsResolvedWhenSecondPromiseIsRejectedThenFirstPromiseIsResolved }) .Forget(); - deferred2.TryReject("Error"); + tryCompleter2(); Assert.IsFalse(resolved); @@ -236,8 +236,8 @@ public void FirstIsRejectedWhenAllPromisesAreRejected_void( bool rejected = false; string expected = "Error"; - var rejectPromise1 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Different Error", out var deferred1, out _); - var rejectPromise2 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, expected, out var deferred2, out _); + var rejectPromise1 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Different Error", out var tryCompleter1); + var rejectPromise2 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, expected, out var tryCompleter2); Promise.First(rejectPromise1, rejectPromise2) .Catch((string rej) => @@ -247,11 +247,11 @@ public void FirstIsRejectedWhenAllPromisesAreRejected_void( }) .Forget(); - deferred1.TryReject("Different Error"); + tryCompleter1(); Assert.AreEqual(alreadyRejected, rejected); - deferred2.TryReject(expected); + tryCompleter2(); Assert.IsTrue(rejected); } @@ -263,8 +263,8 @@ public void FirstIsRejectedWhenAllPromisesAreRejected_T( bool rejected = false; string expected = "Error"; - var rejectPromise1 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Different Error", out var deferred1, out _); - var rejectPromise2 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, expected, out var deferred2, out _); + var rejectPromise1 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Different Error", out var tryCompleter1); + var rejectPromise2 = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, expected, out var tryCompleter2); Promise.First(rejectPromise1, rejectPromise2) .Catch((string rej) => @@ -274,11 +274,11 @@ public void FirstIsRejectedWhenAllPromisesAreRejected_T( }) .Forget(); - deferred1.TryReject("Different Error"); + tryCompleter1(); Assert.AreEqual(alreadyRejected, rejected); - deferred2.TryReject(expected); + tryCompleter2(); Assert.IsTrue(rejected); } @@ -288,8 +288,8 @@ public void FirstIsResolvedWhenFirstPromiseIsCanceledThenSecondPromiseIsResolved [Values] bool alreadyCanceled, [Values] bool alreadyResolved) { - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred1, out _); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter1); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -300,11 +300,11 @@ public void FirstIsResolvedWhenFirstPromiseIsCanceledThenSecondPromiseIsResolved }) .Forget(); - deferred1.TryCancel(); + tryCompleter1(); Assert.AreEqual(alreadyResolved, resolved); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -314,8 +314,8 @@ public void FirstIsResolvedWhenFirstPromiseIsCanceledThenSecondPromiseIsResolved [Values] bool alreadyCanceled, [Values] bool alreadyResolved) { - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 2, "Error", out var deferred1, out _); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 2, "Error", out var tryCompleter1); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -327,11 +327,11 @@ public void FirstIsResolvedWhenFirstPromiseIsCanceledThenSecondPromiseIsResolved }) .Forget(); - deferred1.TryCancel(); + tryCompleter1(); Assert.AreEqual(alreadyResolved, resolved); - deferred2.TryResolve(5); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -341,7 +341,7 @@ public void FirstIsResolvedWhenSecondPromiseIsCanceledThenFirstPromiseIsResolved [Values] bool alreadyCanceled) { var deferred1 = Promise.NewDeferred(); - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter2); bool resolved = false; @@ -352,7 +352,7 @@ public void FirstIsResolvedWhenSecondPromiseIsCanceledThenFirstPromiseIsResolved }) .Forget(); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsFalse(resolved); @@ -366,7 +366,7 @@ public void FirstIsResolvedWhenSecondPromiseIsCanceledThenFirstPromiseIsResolved [Values] bool alreadyCanceled) { var deferred1 = Promise.NewDeferred(); - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -378,7 +378,7 @@ public void FirstIsResolvedWhenSecondPromiseIsCanceledThenFirstPromiseIsResolved }) .Forget(); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsFalse(resolved); @@ -391,8 +391,8 @@ public void FirstIsResolvedWhenSecondPromiseIsCanceledThenFirstPromiseIsResolved public void FirstIsCanceledWhenAllPromisesAreCanceled_void( [Values] bool alreadyCanceled) { - var cancelPromise1 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred1, out _); - var cancelPromise2 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred2, out _); + var cancelPromise1 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter1); + var cancelPromise2 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter2); bool canceled = false; @@ -403,10 +403,10 @@ public void FirstIsCanceledWhenAllPromisesAreCanceled_void( }) .Forget(); - deferred1.TryCancel(); + tryCompleter1(); Assert.AreEqual(alreadyCanceled, canceled); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsTrue(canceled); } @@ -414,8 +414,8 @@ public void FirstIsCanceledWhenAllPromisesAreCanceled_void( public void FirstIsCanceledWhenAllPromisesAreCanceled_T( [Values] bool alreadyCanceled) { - var cancelPromise1 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var deferred1, out _); - var cancelPromise2 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var deferred2, out _); + var cancelPromise1 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var tryCompleter1); + var cancelPromise2 = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var tryCompleter2); bool canceled = false; @@ -426,10 +426,10 @@ public void FirstIsCanceledWhenAllPromisesAreCanceled_T( }) .Forget(); - deferred1.TryCancel(); + tryCompleter1(); Assert.AreEqual(alreadyCanceled, canceled); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsTrue(canceled); } @@ -665,20 +665,13 @@ public void FirstIsCancelededWhenSecondPromiseIsRejectedThenFirstPromiseIsCancel cancelationSource.Dispose(); } - private static void Swap(ref T a, ref T b) - { - var temp = a; - a = b; - b = temp; - } - [Test] public void FirstWithIndex_2_void( [Values(0, 1)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); int resultIndex = -1; @@ -688,10 +681,10 @@ public void FirstWithIndex_2_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); + tryCompleter1(); + tryCompleter2(); Assert.AreEqual(winIndex, resultIndex); } @@ -701,9 +694,9 @@ public void FirstWithIndex_3_void( [Values(0, 1, 2)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); int resultIndex = -1; @@ -713,15 +706,15 @@ public void FirstWithIndex_3_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); Assert.AreEqual(winIndex, resultIndex); } @@ -731,10 +724,10 @@ public void FirstWithIndex_4_void( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var tryCompleter4); int resultIndex = -1; @@ -744,20 +737,20 @@ public void FirstWithIndex_4_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); - deferred4.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); } @@ -767,10 +760,10 @@ public void FirstWithIndex_array_void( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var tryCompleter4); int resultIndex = -1; @@ -780,20 +773,20 @@ public void FirstWithIndex_array_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); - deferred4.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); } @@ -803,8 +796,8 @@ public void FirstWithIndex_2_T( [Values(0, 1)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); int resultIndex = -1; int result = -1; @@ -819,10 +812,10 @@ public void FirstWithIndex_2_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); + tryCompleter1(); + tryCompleter2(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -833,9 +826,9 @@ public void FirstWithIndex_3_T( [Values(0, 1, 2)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); int resultIndex = -1; int result = -1; @@ -850,15 +843,15 @@ public void FirstWithIndex_3_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -869,10 +862,10 @@ public void FirstWithIndex_4_T( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, alreadyResolved && winIndex == 3 ? 1 : 4, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, winIndex == 3 ? 1 : 4, "Error", out var tryCompleter4); int resultIndex = -1; int result = -1; @@ -887,20 +880,20 @@ public void FirstWithIndex_4_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); - deferred4.TryResolve(4); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -911,10 +904,10 @@ public void FirstWithIndex_array_T( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, alreadyResolved && winIndex == 3 ? 1 : 4, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, winIndex == 3 ? 1 : 4, "Error", out var tryCompleter4); int resultIndex = -1; int result = -1; @@ -929,20 +922,20 @@ public void FirstWithIndex_array_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); - deferred4.TryResolve(4); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); diff --git a/Package/Tests/CoreTests/APIs/Linq/AsyncEnumerableTests.cs b/Package/Tests/CoreTests/APIs/Linq/AsyncEnumerableTests.cs index 35baec75..29ca172e 100644 --- a/Package/Tests/CoreTests/APIs/Linq/AsyncEnumerableTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/AsyncEnumerableTests.cs @@ -417,14 +417,7 @@ public void AsyncEnumerableRespectsConfigureAwait( : 0; for (int i = 0; i < awaitCount; i++) { - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return didAwaitDeferred; - }, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => didAwaitDeferred, TimeSpan.FromSeconds(1)); didAwaitDeferred = false; Assert.False(runnerIsComplete); var def = deferred; @@ -434,14 +427,7 @@ public void AsyncEnumerableRespectsConfigureAwait( if (iteratorIsAsync || consumerIsAsync) { - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return didAwaitDeferred; - }, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => didAwaitDeferred, TimeSpan.FromSeconds(1)); Assert.False(runnerIsComplete); } deferred.Resolve(); diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySeedSelectorTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySeedSelectorTests.cs index b8dcd318..661fde46 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySeedSelectorTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySeedSelectorTests.cs @@ -1001,9 +1001,10 @@ private static IEqualityComparer GetDefaultOrNullComparer(bool defaultComp public void AggregateBy_Empty( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureseed, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureseed, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -1110,9 +1111,10 @@ public void AggregateBy_Expected2( public void AggregateBy_Throws_Source( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureseed, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureseed, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -1134,8 +1136,9 @@ public void AggregateBy_KeySelectorThrows( [Values] bool configured, [Values] bool async, [Values] bool captureKey, - [Values] bool captureseed, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureseed, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -1156,8 +1159,9 @@ public void AggregateBy_KeySelectorThrows( public void AggregateBy_AccumulatorThrows( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureseed, + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureseed, [Values] bool captureAccumulate, [Values] bool withComparer) { @@ -1179,9 +1183,10 @@ public void AggregateBy_AccumulatorThrows( public void AggregateBy_SeedSelectorThrows( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool captureseed, - [Values] bool captureAccumulate, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySingleSeedTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySingleSeedTests.cs index 2b776b11..bb791fdb 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySingleSeedTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/AggregateBySingleSeedTests.cs @@ -426,8 +426,9 @@ private static IEqualityComparer GetDefaultOrNullComparer(bool defaultComp public void AggregateBy_Empty( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -531,8 +532,9 @@ public void AggregateBy_Expected2( public void AggregateBy_Throws_Source( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -554,7 +556,8 @@ public void AggregateBy_KeySelectorThrows( [Values] bool configured, [Values] bool async, [Values] bool captureKey, - [Values] bool captureAccumulate, + // Reduce number of tests. + [Values(false)] bool captureAccumulate, [Values] bool withComparer) { Promise.Run(async () => @@ -575,7 +578,8 @@ public void AggregateBy_KeySelectorThrows( public void AggregateBy_AccumulatorThrows( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool captureAccumulate, [Values] bool withComparer) { diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/GroupByTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/GroupByTests.cs index d5e7947d..8b5855cb 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/GroupByTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/GroupByTests.cs @@ -604,7 +604,8 @@ public void GroupBy_KeySelector_Simple1( public void GroupBy_KeySelector_Simple2_TempCollectionIsStillValidAfterMoveNextAsyncUntilDisposeAsync( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool withComparer) { Promise.Run(async () => @@ -665,7 +666,8 @@ public void GroupBy_KeySelector_Simple2_TempCollectionIsStillValidAfterMoveNextA public void GroupBy_KeySelector_Simple2_TempCollectionToArrayIsPersistedAfterMoveNextAndDisposeAsync( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool withComparer) { Promise.Run(async () => @@ -726,7 +728,8 @@ public void GroupBy_KeySelector_Simple2_TempCollectionToArrayIsPersistedAfterMov public void GroupBy_KeySelector_Empty( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool withComparer) { Promise.Run(async () => @@ -742,7 +745,8 @@ public void GroupBy_KeySelector_Empty( public void GroupBy_KeySelector_Throws_Source1( [Values] bool configured, [Values] bool async, - [Values] bool captureKey) + // Reduce number of tests. + [Values(false)] bool captureKey) { Promise.Run(async () => { @@ -758,7 +762,8 @@ public void GroupBy_KeySelector_Throws_Source1( public void GroupBy_KeySelector_Throws_Source2( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, + // Reduce number of tests. + [Values(false)] bool captureKey, [Values] bool withComparer) { Promise.Run(async () => @@ -983,8 +988,9 @@ public void GroupBy_KeySelector_ElementSelector_Comparer_Simple1( public void GroupBy_KeySelector_ElementSelector_Comparer_TempCollectionIsInvalidatedAfterDisposeAsync( [Values] bool configured, [Values] bool async, - [Values] bool captureKey, - [Values] bool captureElement) + // Reduce number of tests. + [Values(false)] bool captureKey, + [Values(false)] bool captureElement) { Promise.Run(async () => { diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/GroupJoinTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/GroupJoinTests.cs index ffe0ed25..b5ae9596 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/GroupJoinTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/GroupJoinTests.cs @@ -412,7 +412,8 @@ public void GroupJoin_EmptyBoth( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -431,7 +432,8 @@ public void GroupJoin_EmptyBoth( public void GroupJoin_EmptyOuter( [Values] bool configured, [Values] bool async, - [Values] bool captureOuterKey, + // Reduce number of tests. + [Values(false)] bool captureOuterKey, [Values] bool captureInnerKey, [Values] bool withComparer) { @@ -452,7 +454,8 @@ public void GroupJoin_EmptyInner( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -540,7 +543,8 @@ public void GroupJoin_OuterThrows( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -562,7 +566,8 @@ public void GroupJoin_InnerThrows( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -584,7 +589,8 @@ public void GroupJoin_OuterKeySelectorThrows( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -606,7 +612,8 @@ public void GroupJoin_InnerKeySelectorThrows( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/JoinTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/JoinTests.cs index 91f89d0d..8c326529 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/JoinTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/JoinTests.cs @@ -411,7 +411,8 @@ public void Join_EmptyOuter( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -431,7 +432,8 @@ public void Join_EmptyInner( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -554,7 +556,8 @@ public void Join5Async( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -576,7 +579,8 @@ public void Join6Async( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -598,7 +602,8 @@ public void Join7Async( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -620,7 +625,8 @@ public void Join8Async( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -642,7 +648,8 @@ public void Join11( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => @@ -691,7 +698,8 @@ public void Join12( [Values] bool configured, [Values] bool async, [Values] bool captureOuterKey, - [Values] bool captureInnerKey, + // Reduce number of tests. + [Values(false)] bool captureInnerKey, [Values] bool withComparer) { Promise.Run(async () => diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/MergeTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/MergeTests.cs index ea130534..4a9666e4 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/MergeTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/MergeTests.cs @@ -51,146 +51,144 @@ private static AsyncEnumerable EnumerableRangeAsync(int start, int count, b public void AsyncEnumerableMerge_MergesConcurrently_Async() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; + if (i > start) + { + await promiseRetainer; + } + + await writer.YieldAsync(i); } + }); + }; - await writer.YieldAsync(i); - } + var enumerablesAsync = AsyncEnumerable.Create>(async (writer, cancelationToken) => + { + await writer.YieldAsync(EnumerableRangeAsync(0, 1)); + await writer.YieldAsync(EnumerableRangeAsync(0, 2)); + await writer.YieldAsync(EnumerableRangeAsync(0, 3)); + await writer.YieldAsync(EnumerableRangeAsync(0, 4)); }); - }; - - var enumerablesAsync = AsyncEnumerable.Create>(async (writer, cancelationToken) => - { - await writer.YieldAsync(EnumerableRangeAsync(0, 1)); - await writer.YieldAsync(EnumerableRangeAsync(0, 2)); - await writer.YieldAsync(EnumerableRangeAsync(0, 3)); - await writer.YieldAsync(EnumerableRangeAsync(0, 4)); - }); - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; - int twoCount = 0; - int threeCount = 0; + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; + int twoCount = 0; + int threeCount = 0; - var runPromise = AsyncEnumerable.Merge(enumerablesAsync) - .ForEachAsync(num => - { - ++totalCount; - if (totalCount <= 4) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else if (totalCount <= 4 + 3) + var runPromise = AsyncEnumerable.Merge(enumerablesAsync) + .ForEachAsync(num => { - ++oneCount; - } - else if (totalCount <= 4 + 3 + 2) - { - ++twoCount; - } - else - { - ++threeCount; - } - }); - - Assert.AreEqual(4, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 4) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else if (totalCount <= 4 + 3) + { + ++oneCount; + } + else if (totalCount <= 4 + 3 + 2) + { + ++twoCount; + } + else + { + ++threeCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(4, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 4); - Assert.AreEqual(oneCount, 3); - Assert.AreEqual(twoCount, 2); - Assert.AreEqual(threeCount, 1); - Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 4); + Assert.AreEqual(oneCount, 3); + Assert.AreEqual(twoCount, 2); + Assert.AreEqual(threeCount, 1); + Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + } } [Test] public void AsyncEnumerableMerge_MergesConcurrently_Array_Sync() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; - } + if (i > start) + { + await promiseRetainer; + } - await writer.YieldAsync(i); - } - }); - }; - - var enumerables = new AsyncEnumerable[4] - { - EnumerableRangeAsync(0, 1), - EnumerableRangeAsync(0, 2), - EnumerableRangeAsync(0, 3), - EnumerableRangeAsync(0, 4) - }; - - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; - int twoCount = 0; - int threeCount = 0; + await writer.YieldAsync(i); + } + }); + }; - var runPromise = AsyncEnumerable.Merge(enumerables) - .ForEachAsync(num => + var enumerables = new AsyncEnumerable[4] { - ++totalCount; - if (totalCount <= 4) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else if (totalCount <= 4 + 3) - { - ++oneCount; - } - else if (totalCount <= 4 + 3 + 2) - { - ++twoCount; - } - else + EnumerableRangeAsync(0, 1), + EnumerableRangeAsync(0, 2), + EnumerableRangeAsync(0, 3), + EnumerableRangeAsync(0, 4) + }; + + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; + int twoCount = 0; + int threeCount = 0; + + var runPromise = AsyncEnumerable.Merge(enumerables) + .ForEachAsync(num => { - ++threeCount; - } - }); - - Assert.AreEqual(4, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 4) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else if (totalCount <= 4 + 3) + { + ++oneCount; + } + else if (totalCount <= 4 + 3 + 2) + { + ++twoCount; + } + else + { + ++threeCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(4, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 4); - Assert.AreEqual(oneCount, 3); - Assert.AreEqual(twoCount, 2); - Assert.AreEqual(threeCount, 1); - Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 4); + Assert.AreEqual(oneCount, 3); + Assert.AreEqual(twoCount, 2); + Assert.AreEqual(threeCount, 1); + Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + } } // ReadOnlySpan is not available in Unity netstandard2.0, and we can't include nuget package dependencies in Unity packages, @@ -200,73 +198,72 @@ public void AsyncEnumerableMerge_MergesConcurrently_Array_Sync() public void AsyncEnumerableMerge_MergesConcurrently_Span_Sync() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; - } - - await writer.YieldAsync(i); - } - }); - }; + if (i > start) + { + await promiseRetainer; + } - ReadOnlySpan> enumerables = new AsyncEnumerable[4] - { - EnumerableRangeAsync(0, 1), - EnumerableRangeAsync(0, 2), - EnumerableRangeAsync(0, 3), - EnumerableRangeAsync(0, 4) - }; - - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; - int twoCount = 0; - int threeCount = 0; + await writer.YieldAsync(i); + } + }); + }; - var runPromise = AsyncEnumerable.Merge(enumerables) - .ForEachAsync(num => + ReadOnlySpan> enumerables = new AsyncEnumerable[4] { - ++totalCount; - if (totalCount <= 4) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else if (totalCount <= 4 + 3) - { - ++oneCount; - } - else if (totalCount <= 4 + 3 + 2) + EnumerableRangeAsync(0, 1), + EnumerableRangeAsync(0, 2), + EnumerableRangeAsync(0, 3), + EnumerableRangeAsync(0, 4) + }; + + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; + int twoCount = 0; + int threeCount = 0; + + var runPromise = AsyncEnumerable.Merge(enumerables) + .ForEachAsync(num => { - ++twoCount; - } - else - { - ++threeCount; - } - }); - - Assert.AreEqual(4, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 4) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else if (totalCount <= 4 + 3) + { + ++oneCount; + } + else if (totalCount <= 4 + 3 + 2) + { + ++twoCount; + } + else + { + ++threeCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(4, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 4); - Assert.AreEqual(oneCount, 3); - Assert.AreEqual(twoCount, 2); - Assert.AreEqual(threeCount, 1); - Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 4); + Assert.AreEqual(oneCount, 3); + Assert.AreEqual(twoCount, 2); + Assert.AreEqual(threeCount, 1); + Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + } } #endif // !UNITY_2018_3_OR_NEWER || UNITY_2021_2_OR_NEWER @@ -274,316 +271,311 @@ public void AsyncEnumerableMerge_MergesConcurrently_Span_Sync() public void AsyncEnumerableMerge_MergesConcurrently_2() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; - } + if (i > start) + { + await promiseRetainer; + } - await writer.YieldAsync(i); - } - }); - }; + await writer.YieldAsync(i); + } + }); + }; - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; - var runPromise = AsyncEnumerable.Merge( - EnumerableRangeAsync(0, 1), - EnumerableRangeAsync(0, 2) - ) - .ForEachAsync(num => - { - ++totalCount; - if (totalCount <= 2) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else + var runPromise = AsyncEnumerable.Merge( + EnumerableRangeAsync(0, 1), + EnumerableRangeAsync(0, 2) + ) + .ForEachAsync(num => { - ++oneCount; - } - }); - - Assert.AreEqual(2, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 2) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else + { + ++oneCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(2, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 2); - Assert.AreEqual(oneCount, 1); - Assert.AreEqual(2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 2); + Assert.AreEqual(oneCount, 1); + Assert.AreEqual(2 + 1, totalCount); + } } [Test] public void AsyncEnumerableMerge_MergesConcurrently_3() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; - } - - await writer.YieldAsync(i); - } - }); - }; + if (i > start) + { + await promiseRetainer; + } - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; - int twoCount = 0; - - var runPromise = AsyncEnumerable.Merge( - EnumerableRangeAsync(0, 1), - EnumerableRangeAsync(0, 2), - EnumerableRangeAsync(0, 3) - ) - .ForEachAsync(num => - { - ++totalCount; - if (totalCount <= 3) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else if (totalCount <= 3 + 2) - { - ++oneCount; - } - else + await writer.YieldAsync(i); + } + }); + }; + + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; + int twoCount = 0; + + var runPromise = AsyncEnumerable.Merge( + EnumerableRangeAsync(0, 1), + EnumerableRangeAsync(0, 2), + EnumerableRangeAsync(0, 3) + ) + .ForEachAsync(num => { - ++twoCount; - } - }); - - Assert.AreEqual(3, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 3) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else if (totalCount <= 3 + 2) + { + ++oneCount; + } + else + { + ++twoCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(3, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 3); - Assert.AreEqual(oneCount, 2); - Assert.AreEqual(twoCount, 1); - Assert.AreEqual(3 + 2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 3); + Assert.AreEqual(oneCount, 2); + Assert.AreEqual(twoCount, 1); + Assert.AreEqual(3 + 2 + 1, totalCount); + } } [Test] public void AsyncEnumerableMerge_MergesConcurrently_4() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Func> EnumerableRangeAsync = (int start, int count) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - return AsyncEnumerable.Create(async (writer, cancelationToken) => + Func> EnumerableRangeAsync = (int start, int count) => { - for (int i = start; i < start + count; i++) + return AsyncEnumerable.Create(async (writer, cancelationToken) => { - if (i > start) + for (int i = start; i < start + count; i++) { - await yieldPromise; - } + if (i > start) + { + await promiseRetainer; + } - await writer.YieldAsync(i); - } - }); - }; - - int totalCount = 0; - int zeroCount = 0; - int oneCount = 0; - int twoCount = 0; - int threeCount = 0; - - var runPromise = AsyncEnumerable.Merge( - EnumerableRangeAsync(0, 1), - EnumerableRangeAsync(0, 2), - EnumerableRangeAsync(0, 3), - EnumerableRangeAsync(0, 4) - ) - .ForEachAsync(num => - { - ++totalCount; - if (totalCount <= 4) - { - Assert.AreEqual(0, num); - ++zeroCount; - } - else if (totalCount <= 4 + 3) - { - ++oneCount; - } - else if (totalCount <= 4 + 3 + 2) - { - ++twoCount; - } - else + await writer.YieldAsync(i); + } + }); + }; + + int totalCount = 0; + int zeroCount = 0; + int oneCount = 0; + int twoCount = 0; + int threeCount = 0; + + var runPromise = AsyncEnumerable.Merge( + EnumerableRangeAsync(0, 1), + EnumerableRangeAsync(0, 2), + EnumerableRangeAsync(0, 3), + EnumerableRangeAsync(0, 4) + ) + .ForEachAsync(num => { - ++threeCount; - } - }); - - Assert.AreEqual(4, totalCount); - deferred.Resolve(); + ++totalCount; + if (totalCount <= 4) + { + Assert.AreEqual(0, num); + ++zeroCount; + } + else if (totalCount <= 4 + 3) + { + ++oneCount; + } + else if (totalCount <= 4 + 3 + 2) + { + ++twoCount; + } + else + { + ++threeCount; + } + }); - runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + Assert.AreEqual(4, totalCount); + deferred.Resolve(); - Assert.AreEqual(zeroCount, 4); - Assert.AreEqual(oneCount, 3); - Assert.AreEqual(twoCount, 2); - Assert.AreEqual(threeCount, 1); - Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(zeroCount, 4); + Assert.AreEqual(oneCount, 3); + Assert.AreEqual(twoCount, 2); + Assert.AreEqual(threeCount, 1); + Assert.AreEqual(4 + 3 + 2 + 1, totalCount); + } } [Test] public void AsyncEnumerableMerge_CanceledAsyncEnumerable_NotifiesOthersAndCancelsConsumer() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - int notifiedCount = 0; - - Func> ObserveCancelation = () => AsyncEnumerable.Create(async (writer, cancelationToken) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - await yieldPromise; + int notifiedCount = 0; - if (cancelationToken.IsCancelationRequested) + Func> ObserveCancelation = () => AsyncEnumerable.Create(async (writer, cancelationToken) => { - ++notifiedCount; - } - }); - - Func> EnumerableRangeAsyncWithCancelation = (int start, int count) => AsyncEnumerable.Create(async (writer, cancelationToken) => - { - for (int i = start; i < start + count; i++) - { - cancelationToken.ThrowIfCancelationRequested(); + await promiseRetainer; - if (i > start) + if (cancelationToken.IsCancelationRequested) { - throw Promise.CancelException(); + ++notifiedCount; } + }); - await writer.YieldAsync(i); - } - }); + Func> EnumerableRangeAsyncWithCancelation = (int start, int count) => AsyncEnumerable.Create(async (writer, cancelationToken) => + { + for (int i = start; i < start + count; i++) + { + cancelationToken.ThrowIfCancelationRequested(); - Promise.Run(async () => - { - var asyncEnumerator = AsyncEnumerable.Merge( - ObserveCancelation(), - EnumerableRangeAsyncWithCancelation(0, 2), - ObserveCancelation(), - ObserveCancelation() - ) - .GetAsyncEnumerator(); - Assert.True(await asyncEnumerator.MoveNextAsync()); - Assert.AreEqual(0, asyncEnumerator.Current); - var moveNextPromise = asyncEnumerator.MoveNextAsync(); - deferred.Resolve(); - await TestHelper.AssertCanceledAsync(() => moveNextPromise); + if (i > start) + { + throw Promise.CancelException(); + } - await asyncEnumerator.DisposeAsync(); - }, SynchronizationOption.Synchronous) - .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + await writer.YieldAsync(i); + } + }); + + Promise.Run(async () => + { + var asyncEnumerator = AsyncEnumerable.Merge( + ObserveCancelation(), + EnumerableRangeAsyncWithCancelation(0, 2), + ObserveCancelation(), + ObserveCancelation() + ) + .GetAsyncEnumerator(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(0, asyncEnumerator.Current); + var moveNextPromise = asyncEnumerator.MoveNextAsync(); + deferred.Resolve(); + await TestHelper.AssertCanceledAsync(() => moveNextPromise); - Assert.AreEqual(3, notifiedCount); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(3, notifiedCount); + } } [Test] public void AsyncEnumerableMerge_RejectedAsyncEnumerable_NotifiesOthersAndRejectsConsumer() { var deferred = Promise.NewDeferred(); - var yieldPromise = deferred.Promise.Preserve(); - - Exception expectedException = new Exception("expected"); - int notifiedCount = 0; - - Func> ObserveCancelation = () => AsyncEnumerable.Create(async (writer, cancelationToken) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - await yieldPromise; + Exception expectedException = new Exception("expected"); + int notifiedCount = 0; - if (cancelationToken.IsCancelationRequested) + Func> ObserveCancelation = () => AsyncEnumerable.Create(async (writer, cancelationToken) => { - ++notifiedCount; - } - }); + await promiseRetainer; - Func> EnumerableRangeAsyncWithCancelation = (int start, int count) => AsyncEnumerable.Create(async (writer, cancelationToken) => - { - for (int i = start; i < start + count; i++) - { - cancelationToken.ThrowIfCancelationRequested(); - - if (i > start) + if (cancelationToken.IsCancelationRequested) { - throw expectedException; + ++notifiedCount; } + }); - await writer.YieldAsync(i); - } - }); + Func> EnumerableRangeAsyncWithCancelation = (int start, int count) => AsyncEnumerable.Create(async (writer, cancelationToken) => + { + for (int i = start; i < start + count; i++) + { + cancelationToken.ThrowIfCancelationRequested(); - Promise.Run(async () => - { - var asyncEnumerator = AsyncEnumerable.Merge( - ObserveCancelation(), - EnumerableRangeAsyncWithCancelation(0, 2), - ObserveCancelation(), - ObserveCancelation() - ) - .GetAsyncEnumerator(); - Assert.True(await asyncEnumerator.MoveNextAsync()); - Assert.AreEqual(0, asyncEnumerator.Current); - var moveNextPromise = asyncEnumerator.MoveNextAsync(); - deferred.Resolve(); + if (i > start) + { + throw expectedException; + } - bool rejected = false; - try - { - await moveNextPromise; - } - catch (System.AggregateException e) + await writer.YieldAsync(i); + } + }); + + Promise.Run(async () => { - Assert.AreEqual(1, e.InnerExceptions.Count); - Assert.AreEqual(expectedException, e.InnerException); - rejected = true; - } - Assert.True(rejected); + var asyncEnumerator = AsyncEnumerable.Merge( + ObserveCancelation(), + EnumerableRangeAsyncWithCancelation(0, 2), + ObserveCancelation(), + ObserveCancelation() + ) + .GetAsyncEnumerator(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(0, asyncEnumerator.Current); + var moveNextPromise = asyncEnumerator.MoveNextAsync(); + deferred.Resolve(); - await asyncEnumerator.DisposeAsync(); - }, SynchronizationOption.Synchronous) - .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + bool rejected = false; + try + { + await moveNextPromise; + } + catch (System.AggregateException e) + { + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerException); + rejected = true; + } + Assert.True(rejected); - Assert.AreEqual(3, notifiedCount); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - yieldPromise.Forget(); + Assert.AreEqual(3, notifiedCount); + } } [Test] diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/OrderByTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/OrderByTests.cs index 05c79010..9e7af2d8 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/OrderByTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/OrderByTests.cs @@ -843,7 +843,8 @@ public void OrderByDescendingThenBy( [Values] bool asyncOrderBy, [Values] bool captureKeyOrderBy, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withOrderByComparer, [Values] bool withThenByComparer) { @@ -893,7 +894,8 @@ public void OrderByDescendingThenByDescending( [Values] bool asyncOrderBy, [Values] bool captureKeyOrderBy, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withOrderByComparer, [Values] bool withThenByComparer) { @@ -1036,7 +1038,8 @@ public int Compare(Person x, Person y) public void OrderThenBy( [Values] bool configured, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withComparer) { Promise.Run(async () => @@ -1083,7 +1086,8 @@ public void OrderThenBy( public void OrderThenByDescending( [Values] bool configured, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withComparer) { Promise.Run(async () => @@ -1130,7 +1134,8 @@ public void OrderThenByDescending( public void OrderDescendingThenBy( [Values] bool configured, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withComparer) { Promise.Run(async () => @@ -1177,7 +1182,8 @@ public void OrderDescendingThenBy( public void OrderDescendingThenByDescending( [Values] bool configured, [Values] bool asyncThenBy, - [Values] bool captureKeyThenBy, + // Reduce number of tests. + [Values(false)] bool captureKeyThenBy, [Values] bool withComparer) { Promise.Run(async () => @@ -1244,32 +1250,32 @@ public enum ConfiguredType private static IEnumerable Order_Cancel_Args() { - OrderType[] orderTypes = new OrderType[] + var orderTypes = new[] { OrderType.Ascending, OrderType.Descending, OrderType.ByAscending, OrderType.ByDescending }; - ThenByType[] thenByTypes = new ThenByType[] + var thenByTypes = new[] { ThenByType.None, ThenByType.Ascending, ThenByType.Descending }; - ConfiguredType[] configuredTypes = new ConfiguredType[] + var configuredTypes = new[] { ConfiguredType.NotConfigured, ConfiguredType.Configured, ConfiguredType.ConfiguredWithCancelation }; - bool[] bools = new bool[] { true, false }; + var bools = new[] { true, false }; foreach (var orderType in orderTypes) foreach (var thenByType in thenByTypes) foreach (var configuredType in configuredTypes) foreach (bool async in bools) - foreach (bool captureKey in bools) + foreach (bool captureKey in new[] { false })// bools) // Reduce number of tests. foreach (bool withComparer in bools) foreach (bool enumeratorToken in bools) { diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/SkipAndTakeTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/SkipAndTakeTests.cs index 5741da37..b9582c81 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/SkipAndTakeTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/SkipAndTakeTests.cs @@ -181,7 +181,8 @@ public void SkipTake_Simple_3( [Values(0, 1, 2, 7)] int count1, [Values] SkipOrTakeType skipOrTakeType2, [Values(0, 1, 7)] int count2, - [Values] SkipOrTakeType skipOrTakeType3, + // Reduce number of tests. + [Values(SkipOrTakeType.SkipSimple)] SkipOrTakeType skipOrTakeType3, [Values(1, 7)] int count3) { Promise.Run(async () => diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/ToAsyncEnumerableTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/ToAsyncEnumerableTests.cs index e0bb3107..a7edf1a1 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/ToAsyncEnumerableTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/ToAsyncEnumerableTests.cs @@ -427,14 +427,7 @@ async IAsyncEnumerable GetEnumerable() : 0; for (int i = 0; i < awaitCount; i++) { - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return didAwaitDeferred; - }, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => didAwaitDeferred, TimeSpan.FromSeconds(1)); didAwaitDeferred = false; Assert.False(runnerIsComplete); var def = deferred; @@ -444,14 +437,7 @@ async IAsyncEnumerable GetEnumerable() if (iteratorIsAsync || consumerIsAsync) { - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return didAwaitDeferred; - }, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => didAwaitDeferred, TimeSpan.FromSeconds(1)); Assert.False(runnerIsComplete); } deferred.Resolve(); diff --git a/Package/Tests/CoreTests/APIs/Linq/Operators/UnionTests.cs b/Package/Tests/CoreTests/APIs/Linq/Operators/UnionTests.cs index a21e11b5..2f595184 100644 --- a/Package/Tests/CoreTests/APIs/Linq/Operators/UnionTests.cs +++ b/Package/Tests/CoreTests/APIs/Linq/Operators/UnionTests.cs @@ -645,11 +645,12 @@ public void Union_UnionedBoth3_2( [Values] bool configured1, [Values] bool configured2, [Values] bool configured3, - [Values] bool configured4, + // Reduce number of tests. + [Values(false)] bool configured4, [Values] bool withComparer1, [Values] bool withComparer2, [Values] bool withComparer3, - [Values] bool withComparer4) + [Values(false)] bool withComparer4) { Promise.Run(async () => { @@ -687,11 +688,12 @@ public void Union_UnionedBoth2_3( [Values] bool configured1, [Values] bool configured2, [Values] bool configured3, - [Values] bool configured4, + // Reduce number of tests. + [Values(false)] bool configured4, [Values] bool withComparer1, [Values] bool withComparer2, [Values] bool withComparer3, - [Values] bool withComparer4) + [Values(false)] bool withComparer4) { Promise.Run(async () => { @@ -731,13 +733,14 @@ public void Union_UnionedBoth3_3( [Values] bool configured1, [Values] bool configured2, [Values] bool configured3, - [Values] bool configured4, - [Values] bool configured5, + // Reduce number of tests. + [Values(false)] bool configured4, + [Values(false)] bool configured5, [Values] bool withComparer1, [Values] bool withComparer2, [Values] bool withComparer3, - [Values] bool withComparer4, - [Values] bool withComparer5) + [Values(false)] bool withComparer4, + [Values(false)] bool withComparer5) { Promise.Run(async () => { @@ -778,13 +781,14 @@ public void Union_UnionedBoth2_3_1( [Values] bool configured1, [Values] bool configured2, [Values] bool configured3, - [Values] bool configured4, - [Values] bool configured5, + // Reduce number of tests. + [Values(false)] bool configured4, + [Values(false)] bool configured5, [Values] bool withComparer1, [Values] bool withComparer2, [Values] bool withComparer3, - [Values] bool withComparer4, - [Values] bool withComparer5) + [Values(false)] bool withComparer4, + [Values(false)] bool withComparer5) { Promise.Run(async () => { @@ -1118,21 +1122,21 @@ public void Union_UnionedSecond_Cancel( private static IEnumerable Union_UnionedBoth_Cancel_Args() { - ConfiguredType[] configuredTypes = new ConfiguredType[] + var configuredTypes = new[] { ConfiguredType.NotConfigured, ConfiguredType.Configured, ConfiguredType.ConfiguredWithCancelation }; - bool[] bools = new bool[] { true, false }; - int[] cancelSequences = new int[] { 0, 1, 2, 3 }; + var bools = new[] { true, false }; + var cancelSequences = new[] { 0, 1, 2, 3 }; foreach (var configured1Type in configuredTypes) foreach (var configured2Type in configuredTypes) - foreach (var configured3Type in configuredTypes) + foreach (var configured3Type in new[] { ConfiguredType.NotConfigured })// configuredTypes) // Reduce number of tests. foreach (var withComparer1 in bools) foreach (var withComparer2 in bools) - foreach (var withComparer3 in bools) + foreach (var withComparer3 in new[] { true, })// bools) // Reduce number of tests. foreach (var enumeratorToken in bools) foreach (var cancelSequence in cancelSequences) { diff --git a/Package/Tests/CoreTests/APIs/MergeSettledTests.cs b/Package/Tests/CoreTests/APIs/MergeSettledTests.cs index 2e237c17..a3535428 100644 --- a/Package/Tests/CoreTests/APIs/MergeSettledTests.cs +++ b/Package/Tests/CoreTests/APIs/MergeSettledTests.cs @@ -144,8 +144,8 @@ private static IEnumerable GetArgs() private static IEnumerable GetMergeSettledArgs(bool isGeneric, int index) { - // In order to keep the number of test cases down, we only do all combinations for the first 3 args. - if (index >= 3) + // In order to keep the number of test cases down, we only do all combinations for the first 2 args. + if (index >= 2) { yield return new MergeSettledArg() { @@ -261,7 +261,7 @@ private static Promise ConvertPromise(Promise promise) private const string rejectValue = "reject"; - [Test, TestCaseSource("GetArgs")] + [Test, TestCaseSource(nameof(GetArgs))] public void MergeSettledWorksProperly(MergeSettledArg[] args) { Type[] genericArgTypes = args.Select(arg => arg.Type) diff --git a/Package/Tests/CoreTests/APIs/MergeTests.cs b/Package/Tests/CoreTests/APIs/MergeTests.cs index 89489ca2..170b253d 100644 --- a/Package/Tests/CoreTests/APIs/MergeTests.cs +++ b/Package/Tests/CoreTests/APIs/MergeTests.cs @@ -120,30 +120,34 @@ public void MergePromiseIsRejectedWhenBothPromisesAreRejected() var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var promise1 = deferred1.Promise.Preserve(); - var promise2 = deferred2.Promise.Preserve(); - - promise1.Catch((string _) => { }).Forget(); - promise2.Catch((string _) => { }).Forget(); - - string expected = "Error"; + string reject1 = "Error 1"; + string reject2 = "Error 2"; bool rejected = false; - Promise.Merge(promise1, promise2) + // The second rejection gets sent to the UncaughtRejectionHandler. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + bool uncaught = false; + Promise.Config.UncaughtRejectionHandler = e => + { + Assert.AreEqual(reject2, e.Value); + uncaught = true; + }; + + Promise.Merge(deferred1.Promise, deferred2.Promise) .Catch((string e) => { - Assert.AreEqual(expected, e); + Assert.AreEqual(reject1, e); rejected = true; }) .Forget(); - deferred1.Reject(expected); - deferred2.Reject(expected); + deferred1.Reject(reject1); + deferred2.Reject(reject2); Assert.IsTrue(rejected); + Assert.True(uncaught); - promise1.Forget(); - promise2.Forget(); + Promise.Config.UncaughtRejectionHandler = currentHandler; } [Test] diff --git a/Package/Tests/CoreTests/APIs/MiscellaneousTests.cs b/Package/Tests/CoreTests/APIs/MiscellaneousTests.cs index d0974f63..5907765b 100644 --- a/Package/Tests/CoreTests/APIs/MiscellaneousTests.cs +++ b/Package/Tests/CoreTests/APIs/MiscellaneousTests.cs @@ -60,9 +60,12 @@ public void PromiseIsInvalidAfterAwaited_void() #if PROMISE_DEBUG Assert.Throws(() => promise.GetAwaiter()); #endif +#pragma warning disable CS0618 // Type or member is obsolete Assert.Throws(() => promise.Preserve()); - Assert.Throws(() => promise.Forget()); Assert.Throws(() => promise.Duplicate()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.Throws(() => promise.GetRetainer()); + Assert.Throws(() => promise.Forget()); Assert.Throws(() => promise.CatchCancelation(() => { })); Assert.Throws(() => promise.CatchCancelation(1, cv => { })); @@ -187,9 +190,12 @@ public void PromiseIsInvalidAfterAwaited_T() #if PROMISE_DEBUG Assert.Throws(() => promise.GetAwaiter()); #endif +#pragma warning disable CS0618 // Type or member is obsolete Assert.Throws(() => promise.Preserve()); - Assert.Throws(() => promise.Forget()); Assert.Throws(() => promise.Duplicate()); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.Throws(() => promise.GetRetainer()); + Assert.Throws(() => promise.Forget()); Assert.Throws(() => promise.CatchCancelation(() => { })); Assert.Throws(() => promise.CatchCancelation(1, cv => { })); @@ -302,390 +308,380 @@ public void PromiseIsInvalidAfterAwaited_T() [Test] public void ThrowingRejectExceptionInOnResolvedRejectsThePromiseWithTheGivenValue_void() { - var promise = Promise.Resolved().Preserve(); - - int rejectCount = 0; - string expected = "Reject!"; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => + using (var promiseRetainer = Promise.Resolved().GetRetainer()) { - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - }; - - TestHelper.AddResolveCallbacks(promise, - onResolve: () => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(promise, - onResolve: () => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, - rejectCount - ); + int rejectCount = 0; + string expected = "Reject!"; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + { + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + }; + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, + rejectCount + ); + } } [Test] public void ThrowingRejectExceptionInOnResolvedRejectsThePromiseWithTheGivenValue_T() { - var promise = Promise.Resolved(100).Preserve(); - - int rejectCount = 0; - string expected = "Reject!"; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: v => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(promise, - onResolve: v => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, - rejectCount - ); + using (var promiseRetainer = Promise.Resolved(100).GetRetainer()) + { + int rejectCount = 0; + string expected = "Reject!"; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, + rejectCount + ); + } } [Test] public void ThrowingRejectExceptionInOnRejectedRejectsThePromiseWithTheGivenValue_void() { - var promise = Promise.Rejected("A different reject value.").Preserve(); - - int rejectCount = 0; - string expected = "Reject!"; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - - TestHelper.AddCallbacks(promise, - onReject: v => { throw Promise.RejectException(expected); }, - onUnknownRejection: () => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.rejectVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, - rejectCount - ); + using (var promiseRetainer = Promise.Rejected("A different reject value.").GetRetainer()) + { + int rejectCount = 0; + string expected = "Reject!"; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: v => { throw Promise.RejectException(expected); }, + onUnknownRejection: () => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.rejectVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, + rejectCount + ); + } } [Test] public void ThrowingRejectExceptionInOnRejectedRejectsThePromiseWithTheGivenValue_T() { - var promise = Promise.Rejected("A different reject value.").Preserve(); - - int rejectCount = 0; - string expected = "Reject!"; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - - TestHelper.AddCallbacks(promise, - onReject: v => { throw Promise.RejectException(expected); }, - onUnknownRejection: () => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.RejectException(expected); }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.rejectTCallbacks + TestHelper.continueTCallbacks) * 2, - rejectCount - ); + using (var promiseRetainer = Promise.Rejected("A different reject value.").GetRetainer()) + { + int rejectCount = 0; + string expected = "Reject!"; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: v => { throw Promise.RejectException(expected); }, + onUnknownRejection: () => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.RejectException(expected); }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.rejectTCallbacks + TestHelper.continueTCallbacks) * 2, + rejectCount + ); + } } [Test] public void ThrowingCancelExceptionInOnResolvedCancelsThePromiseWithTheGivenValue_void() { - var promise = Promise.Resolved().Preserve(); - - int cancelCount = 0; - - System.Action onCancel = () => + using (var promiseRetainer = Promise.Resolved().GetRetainer()) { - ++cancelCount; - }; - - TestHelper.AddResolveCallbacks(promise, - onResolve: () => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddCallbacks(promise, - onResolve: () => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - - Assert.AreEqual( - (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, - cancelCount - ); + int cancelCount = 0; - promise.Forget(); + System.Action onCancel = () => + { + ++cancelCount; + }; + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + + Assert.AreEqual( + (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, + cancelCount + ); + } } [Test] public void ThrowingCancelExceptionInOnResolvedCancelsThePromiseWithTheGivenValue_T() { - var promise = Promise.Resolved(100).Preserve(); - - int cancelCount = 0; - - System.Action onCancel = () => + using (var promiseRetainer = Promise.Resolved(100).GetRetainer()) { - ++cancelCount; - }; - - TestHelper.AddResolveCallbacks(promise, - onResolve: v => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddCallbacks(promise, - onResolve: v => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - - Assert.AreEqual( - (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, - cancelCount - ); + int cancelCount = 0; - promise.Forget(); + System.Action onCancel = () => + { + ++cancelCount; + }; + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + + Assert.AreEqual( + (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, + cancelCount + ); + } } [Test] public void ThrowingCancelExceptionInOnRejectedCancelsThePromiseWithTheGivenValue_void() { - var promise = Promise.Rejected("Rejected").Preserve(); - - int cancelCount = 0; - - System.Action onCancel = () => + using (var promiseRetainer = Promise.Rejected("Rejected").GetRetainer()) { - ++cancelCount; - }; - - TestHelper.AddCallbacks(promise, - onReject: v => { throw Promise.CancelException(); }, - onUnknownRejection: () => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - - Assert.AreEqual( - (TestHelper.rejectVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, - cancelCount - ); + int cancelCount = 0; - promise.Forget(); + System.Action onCancel = () => + { + ++cancelCount; + }; + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: v => { throw Promise.CancelException(); }, + onUnknownRejection: () => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + + Assert.AreEqual( + (TestHelper.rejectVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, + cancelCount + ); + } } [Test] public void ThrowingCancelExceptionInOnRejectedCancelsThePromiseWithTheGivenValue_T() { - var promise = Promise.Rejected("Rejected").Preserve(); - - int cancelCount = 0; - - System.Action onCancel = () => + using (var promiseRetainer = Promise.Rejected("Rejected").GetRetainer()) { - ++cancelCount; - }; - - TestHelper.AddCallbacks(promise, - onReject: v => { throw Promise.CancelException(); }, - onUnknownRejection: () => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.CancelException(); }, - onCancel: onCancel - ); - - Assert.AreEqual( - (TestHelper.rejectTCallbacks + TestHelper.continueTCallbacks) * 2, - cancelCount - ); + int cancelCount = 0; - promise.Forget(); + System.Action onCancel = () => + { + ++cancelCount; + }; + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: v => { throw Promise.CancelException(); }, + onUnknownRejection: () => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.CancelException(); }, + onCancel: onCancel + ); + + Assert.AreEqual( + (TestHelper.rejectTCallbacks + TestHelper.continueTCallbacks) * 2, + cancelCount + ); + } } [Test] public void ThrowingRethrowInOnResolvedRejectsThePromiseWithInvalidOperationException_void() { - var promise = Promise.Resolved().Preserve(); - - int errorCount = 0; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((object e) => - { - Assert.IsInstanceOf(typeof(InvalidOperationException), e); - ++errorCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((object e) => - { - Assert.IsInstanceOf(typeof(InvalidOperationException), e); - ++errorCount; - }).Forget(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: () => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(promise, - onResolve: () => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, - errorCount - ); + using (var promiseRetainer = Promise.Resolved().GetRetainer()) + { + int errorCount = 0; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((object e) => + { + Assert.IsInstanceOf(typeof(InvalidOperationException), e); + ++errorCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((object e) => + { + Assert.IsInstanceOf(typeof(InvalidOperationException), e); + ++errorCount; + }).Forget(); + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: () => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.resolveVoidCallbacks + TestHelper.continueVoidCallbacks) * 2, + errorCount + ); + } } [Test] public void ThrowingRethrowInOnResolvedRejectsThePromiseWithInvalidOperationException_T() { - var promise = Promise.Resolved(100).Preserve(); - - int errorCount = 0; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((object e) => - { - Assert.IsInstanceOf(typeof(InvalidOperationException), e); - ++errorCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((object e) => - { - Assert.IsInstanceOf(typeof(InvalidOperationException), e); - ++errorCount; - }).Forget(); - - TestHelper.AddResolveCallbacks(promise, - onResolve: v => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddCallbacks(promise, - onResolve: v => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - TestHelper.AddContinueCallbacks(promise, - onContinue: _ => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - Assert.AreEqual( - (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, - errorCount - ); + using (var promiseRetainer = Promise.Resolved(100).GetRetainer()) + { + int errorCount = 0; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((object e) => + { + Assert.IsInstanceOf(typeof(InvalidOperationException), e); + ++errorCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((object e) => + { + Assert.IsInstanceOf(typeof(InvalidOperationException), e); + ++errorCount; + }).Forget(); + + TestHelper.AddResolveCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onResolve: v => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + TestHelper.AddContinueCallbacks(promiseRetainer.WaitAsync(), + onContinue: _ => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + Assert.AreEqual( + (TestHelper.resolveTCallbacks + TestHelper.continueTCallbacks) * 2, + errorCount + ); + } } [Test] @@ -694,41 +690,38 @@ public void ThrowingRethrowInOnRejectedRejectsThePromiseWithTheSameReason_void( { string expected = "Reject!"; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var promise = TestHelper.BuildPromise(CompleteType.Reject, alreadyComplete, expected, out deferred, out cancelationSource) - .Preserve(); - - int rejectCount = 0; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - - TestHelper.AddCallbacks(promise, - onReject: _ => { throw Promise.Rethrow; }, - onUnknownRejection: () => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert - ); - - deferred.TryReject(expected); - - Assert.AreEqual( - TestHelper.rejectVoidCallbacks * 2, - rejectCount - ); + using (var promiseRetainer = TestHelper.BuildPromise(CompleteType.Reject, alreadyComplete, expected, out var tryCompleter) + .GetRetainer()) + { + int rejectCount = 0; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: _ => { throw Promise.Rethrow; }, + onUnknownRejection: () => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert + ); + + tryCompleter(); + + Assert.AreEqual( + TestHelper.rejectVoidCallbacks * 2, + rejectCount + ); + } } [Test] @@ -737,42 +730,39 @@ public void ThrowingRethrowInOnRejectedRejectsThePromiseWithTheSameReason_T( { string expected = "Reject!"; - CancelationSource cancelationSource; - Promise.Deferred deferred; - var promise = TestHelper.BuildPromise(CompleteType.Reject, alreadyComplete, 0, expected, out deferred, out cancelationSource) - .Preserve(); - - int rejectCount = 0; - - TestAction onCallbackAdded = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - TestAction> onCallbackAddedConvert = (ref Promise p) => - p.Catch((string e) => - { - Assert.AreEqual(expected, e); - ++rejectCount; - }).Forget(); - - TestHelper.AddCallbacks(promise, - onReject: _ => { throw Promise.Rethrow; }, - onUnknownRejection: () => { throw Promise.Rethrow; }, - onCallbackAdded: onCallbackAdded, - onCallbackAddedConvert: onCallbackAddedConvert, - onCallbackAddedT: onCallbackAddedConvert - ); - - deferred.TryReject(expected); - - Assert.AreEqual( - TestHelper.rejectTCallbacks * 2, - rejectCount - ); + using (var promiseRetainer = TestHelper.BuildPromise(CompleteType.Reject, alreadyComplete, 0, expected, out var tryCompleter) + .GetRetainer()) + { + int rejectCount = 0; - promise.Forget(); + TestAction onCallbackAdded = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + TestAction> onCallbackAddedConvert = (ref Promise p) => + p.Catch((string e) => + { + Assert.AreEqual(expected, e); + ++rejectCount; + }).Forget(); + + TestHelper.AddCallbacks(promiseRetainer.WaitAsync(), + onReject: _ => { throw Promise.Rethrow; }, + onUnknownRejection: () => { throw Promise.Rethrow; }, + onCallbackAdded: onCallbackAdded, + onCallbackAddedConvert: onCallbackAddedConvert, + onCallbackAddedT: onCallbackAddedConvert + ); + + tryCompleter(); + + Assert.AreEqual( + TestHelper.rejectTCallbacks * 2, + rejectCount + ); + } } [Test] @@ -1008,7 +998,7 @@ private static bool WaitNoThrow(Promise promise, out Promise.ResultCont [Test] public void PromiseWait_AlreadyCompleted_ReturnsSuccessfullyOrThrowsCorrectException( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values] bool withTimeout) { var expectedException = completeType == CompleteType.Reject @@ -1043,7 +1033,7 @@ public void PromiseWait_AlreadyCompleted_ReturnsSuccessfullyOrThrowsCorrectExcep [Test] public void PromiseWaitNoThrow_AlreadyCompleted_ReturnsSuccessfullyWithProperResult( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values] bool withTimeout) { string expectedRejection = "Rejected"; @@ -1077,7 +1067,7 @@ public void PromiseWait_DoesNotReturnUntilOperationIsComplete( isComplete = true; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1105,7 +1095,7 @@ public void PromiseWaitNoThrow_DoesNotReturnUntilOperationIsComplete( isComplete = true; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1145,7 +1135,7 @@ public void PromiseWait_DoesNotReturnUntilOperationIsComplete_AndThrowsCorrectEx throw expectedException; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1197,7 +1187,7 @@ public void PromiseWaitNoThrow_DoesNotReturnUntilOperationIsComplete_WithCorrect throw expectedException; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1219,7 +1209,7 @@ public void PromiseWaitNoThrow_DoesNotReturnUntilOperationIsComplete_WithCorrect [Test] public void PromiseWaitForResult_AlreadyCompleted_ReturnsSuccessfullyOrThrowsCorrectException( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values] bool withTimeout) { var expectedException = completeType == CompleteType.Reject @@ -1260,7 +1250,7 @@ public void PromiseWaitForResult_AlreadyCompleted_ReturnsSuccessfullyOrThrowsCor [Test] public void PromiseWaitForResultNoThrow_AlreadyCompleted_ReturnsSuccessfullyWithProperResult( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values] bool withTimeout) { var expectedException = completeType == CompleteType.Reject @@ -1303,7 +1293,7 @@ public void PromiseWaitForResult_DoesNotReturnUntilOperationIsComplete_AndReturn return expected; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1338,7 +1328,7 @@ public void PromiseWaitForResultNoThrow_DoesNotReturnUntilOperationIsComplete_An return expected; }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1382,7 +1372,7 @@ public void PromiseWaitForResult_DoesNotReturnUntilOperationIsComplete_AndThrows #pragma warning restore CS0162 // Unreachable code detected }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1438,7 +1428,7 @@ public void PromiseWaitForResultNoThrow_DoesNotReturnUntilOperationIsComplete_Wi #pragma warning restore CS0162 // Unreachable code detected }); - SpinWait.SpinUntil(() => isExecuting); + TestHelper.SpinUntil(() => isExecuting, System.TimeSpan.FromSeconds(1)); if (alreadyComplete) { Thread.Sleep(sleepTime.Add(sleepTime)); @@ -1463,7 +1453,9 @@ public void PromiseWaitForResultNoThrow_DoesNotReturnUntilOperationIsComplete_Wi public void ClearObjectPool_NoErrors() { var deferred = Promise.NewDeferred(); +#pragma warning disable CS0618 // Type or member is obsolete var promise = deferred.Promise.Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete promise .Then(() => { }) @@ -1472,7 +1464,63 @@ public void ClearObjectPool_NoErrors() deferred.Resolve(); promise.Forget(); + deferred = Promise.NewDeferred(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + promiseRetainer.WaitAsync() + .Then(() => { }) + .Forget(); + } + + deferred.Resolve(); + Promise.Manager.ClearObjectPool(); } + + [Test] + public void PromiseRetainer_CanAddCallbacksToWaitAsyncPromiseAfterDisposed_void() + { + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); + var promises = new Queue(); + var actions = TestHelper.ResolveActionsVoid() + .Concat(TestHelper.ThenActionsVoid()) + .Concat(TestHelper.CatchActionsVoid()) + .Concat(TestHelper.ContinueWithActionsVoid()); + foreach (var action in actions) + { + promises.Enqueue(promiseRetainer.WaitAsync()); + } + promiseRetainer.Dispose(); + foreach (var action in actions) + { + action.Invoke(promises.Dequeue()); + } + + deferred.Resolve(); + } + + [Test] + public void PromiseRetainer_CanAddCallbacksToWaitAsyncPromiseAfterDisposed_T() + { + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); + var promises = new Queue>(); + var actions = TestHelper.ResolveActions() + .Concat(TestHelper.ThenActions()) + .Concat(TestHelper.CatchActions()) + .Concat(TestHelper.ContinueWithActions()); + foreach (var action in actions) + { + promises.Enqueue(promiseRetainer.WaitAsync()); + } + promiseRetainer.Dispose(); + foreach (var action in actions) + { + action.Invoke(promises.Dequeue()); + } + + deferred.Resolve(1); + } } } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/NewAndRunTests.cs b/Package/Tests/CoreTests/APIs/NewAndRunTests.cs index e8db95c6..b1b339c0 100644 --- a/Package/Tests/CoreTests/APIs/NewAndRunTests.cs +++ b/Package/Tests/CoreTests/APIs/NewAndRunTests.cs @@ -32,7 +32,7 @@ private static IEnumerable GetArgs_New() SynchronizationType[] synchronizationTypes = new SynchronizationType[] { SynchronizationType.Synchronous, - SynchronizationType.Foreground, + //SynchronizationType.Foreground, // Ignore foreground to reduce number of tests, testing explicit is effectively the same. #if !UNITY_WEBGL SynchronizationType.Background, #endif @@ -69,7 +69,7 @@ private static IEnumerable GetArgs_New() } } - [Test, TestCaseSource("GetArgs_New")] + [Test, TestCaseSource(nameof(GetArgs_New))] public void PromiseNewIsInvokedAndCompletedProperly_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -128,13 +128,13 @@ public void PromiseNewIsInvokedAndCompletedProperly_void( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - TestHelper.GetTryCompleterVoid(completeType, "Reject").Invoke(deferred, default(CancelationSource)); + TestHelper.GetTryCompleterVoid(completeType, "Reject").Invoke(deferred); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_New")] + [Test, TestCaseSource(nameof(GetArgs_New))] public void PromiseNewIsInvokedAndCompletedProperly_capture_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -195,13 +195,13 @@ public void PromiseNewIsInvokedAndCompletedProperly_capture_void( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - TestHelper.GetTryCompleterVoid(completeType, "Reject").Invoke(deferred, default(CancelationSource)); + TestHelper.GetTryCompleterVoid(completeType, "Reject").Invoke(deferred); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_New")] + [Test, TestCaseSource(nameof(GetArgs_New))] public void PromiseNewIsInvokedAndCompletedProperly_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -262,13 +262,13 @@ public void PromiseNewIsInvokedAndCompletedProperly_T( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - TestHelper.GetTryCompleterT(completeType, expectedResolveValue, "Reject").Invoke(deferred, default(CancelationSource)); + TestHelper.GetTryCompleterT(completeType, expectedResolveValue, "Reject").Invoke(deferred); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_New")] + [Test, TestCaseSource(nameof(GetArgs_New))] public void PromiseNewIsInvokedAndCompletedProperly_capture_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -331,7 +331,7 @@ public void PromiseNewIsInvokedAndCompletedProperly_capture_T( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - TestHelper.GetTryCompleterT(completeType, expectedResolveValue, "Reject").Invoke(deferred, default(CancelationSource)); + TestHelper.GetTryCompleterT(completeType, expectedResolveValue, "Reject").Invoke(deferred); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); @@ -343,7 +343,7 @@ private static IEnumerable GetArgs_Run() SynchronizationType[] synchronizationTypes = new SynchronizationType[] { SynchronizationType.Synchronous, - SynchronizationType.Foreground, + //SynchronizationType.Foreground, // Ignore foreground to reduce number of tests, testing explicit is effectively the same. #if !UNITY_WEBGL SynchronizationType.Background, #endif @@ -373,7 +373,7 @@ private static IEnumerable GetArgs_Run() } } - [Test, TestCaseSource("GetArgs_Run")] + [Test, TestCaseSource(nameof(GetArgs_Run))] public void PromiseRunIsInvokedAndCompletedProperly_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -431,7 +431,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_void( Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_Run")] + [Test, TestCaseSource(nameof(GetArgs_Run))] public void PromiseRunIsInvokedAndCompletedProperly_capture_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -491,7 +491,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_capture_void( Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_Run")] + [Test, TestCaseSource(nameof(GetArgs_Run))] public void PromiseRunIsInvokedAndCompletedProperly_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -552,7 +552,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_T( Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_Run")] + [Test, TestCaseSource(nameof(GetArgs_Run))] public void PromiseRunIsInvokedAndCompletedProperly_capture_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -640,8 +640,7 @@ private static IEnumerable GetArgs_RunAdopt() { CompleteType.Resolve, CompleteType.Reject, - CompleteType.Cancel, - CompleteType.CancelFromToken + CompleteType.Cancel }; bool[] bools = new bool[] { true, false }; @@ -664,7 +663,7 @@ private static IEnumerable GetArgs_RunAdopt() } } - [Test, TestCaseSource("GetArgs_RunAdopt")] + [Test, TestCaseSource(nameof(GetArgs_RunAdopt))] public void PromiseRunIsInvokedAndCompletedProperly_adopt_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -678,8 +677,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_void( bool invoked = false; string expectedRejectValue = "Reject"; - var cancelationSource = default(CancelationSource); - var deferred = default(Promise.Deferred); + Action tryCompleter = () => { }; System.Func action = () => { @@ -692,7 +690,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_void( } throw Promise.CancelException(); } - return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedRejectValue, out deferred, out cancelationSource); + return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedRejectValue, out tryCompleter); }; var promise = default(Promise); @@ -728,17 +726,13 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_void( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (isPending) - { - TestHelper.GetCompleterVoid(completeType, expectedRejectValue).Invoke(deferred, cancelationSource); - } - cancelationSource.TryDispose(); + tryCompleter(); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_RunAdopt")] + [Test, TestCaseSource(nameof(GetArgs_RunAdopt))] public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_void( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -753,8 +747,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_void( string expectedRejectValue = "Reject"; int captureValue = 10; - var cancelationSource = default(CancelationSource); - var deferred = default(Promise.Deferred); + Action tryCompleter = () => { }; System.Func action = cv => { @@ -768,7 +761,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_void( } throw Promise.CancelException(); } - return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedRejectValue, out deferred, out cancelationSource); + return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedRejectValue, out tryCompleter); }; var promise = default(Promise); @@ -804,17 +797,13 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_void( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (isPending) - { - TestHelper.GetCompleterVoid(completeType, expectedRejectValue).Invoke(deferred, cancelationSource); - } - cancelationSource.TryDispose(); + tryCompleter(); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_RunAdopt")] + [Test, TestCaseSource(nameof(GetArgs_RunAdopt))] public void PromiseRunIsInvokedAndCompletedProperly_adopt_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -829,8 +818,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_T( string expectedRejectValue = "Reject"; int expectedResolveValue = 1; - var cancelationSource = default(CancelationSource); - var deferred = default(Promise.Deferred); + Action tryCompleter = () => { }; System.Func> action = () => { @@ -843,7 +831,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_T( } throw Promise.CancelException(); } - return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedResolveValue, expectedRejectValue, out deferred, out cancelationSource); + return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedResolveValue, expectedRejectValue, out tryCompleter); }; var promise = default(Promise); @@ -880,17 +868,13 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_T( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (isPending) - { - TestHelper.GetCompleterT(completeType, expectedResolveValue, expectedRejectValue).Invoke(deferred, cancelationSource); - } - cancelationSource.TryDispose(); + tryCompleter(); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); } - [Test, TestCaseSource("GetArgs_RunAdopt")] + [Test, TestCaseSource(nameof(GetArgs_RunAdopt))] public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_T( SynchronizationType synchronizationType, SynchronizationType invokeContext, @@ -906,8 +890,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_T( int expectedResolveValue = 1; int captureValue = 10; - var cancelationSource = default(CancelationSource); - var deferred = default(Promise.Deferred); + Action tryCompleter = () => { }; System.Func> action = cv => { @@ -921,7 +904,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_T( } throw Promise.CancelException(); } - return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedResolveValue, expectedRejectValue, out deferred, out cancelationSource); + return TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedResolveValue, expectedRejectValue, out tryCompleter); }; var promise = default(Promise); @@ -958,11 +941,7 @@ public void PromiseRunIsInvokedAndCompletedProperly_adopt_capture_T( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (isPending) - { - TestHelper.GetCompleterT(completeType, expectedResolveValue, expectedRejectValue).Invoke(deferred, cancelationSource); - } - cancelationSource.TryDispose(); + tryCompleter(); TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); Assert.True(invoked); diff --git a/Package/Tests/CoreTests/APIs/ParallelForEachAsyncTests.cs b/Package/Tests/CoreTests/APIs/ParallelForEachAsyncTests.cs index 40f68c5c..7a60ec6b 100644 --- a/Package/Tests/CoreTests/APIs/ParallelForEachAsyncTests.cs +++ b/Package/Tests/CoreTests/APIs/ParallelForEachAsyncTests.cs @@ -137,23 +137,23 @@ public void Dop_WorkersCreatedRespectingLimit_Async( int activeWorkers = 0; var block = Promise.NewDeferred(); - var blockPromise = block.Promise.Preserve(); - - Promise t = Promise.ParallelForEachAsync(iterateUntilSetAsync, (item, cancelationToken) => + using (var blockPromiseRetainer = block.Promise.GetRetainer()) { - Interlocked.Increment(ref activeWorkers); - return blockPromise; - }, maxDegreeOfParallelism: dop); + Promise t = Promise.ParallelForEachAsync(iterateUntilSetAsync, (item, cancelationToken) => + { + Interlocked.Increment(ref activeWorkers); + return blockPromiseRetainer.WaitAsync(); + }, maxDegreeOfParallelism: dop); - Thread.Sleep(20); // give the loop some time to run + Thread.Sleep(20); // give the loop some time to run - box.Value = true; - block.Resolve(); - int maxWorkers = dop == -1 ? Environment.ProcessorCount : dop; - t.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(maxWorkers)); + box.Value = true; + block.Resolve(); + int maxWorkers = dop == -1 ? Environment.ProcessorCount : dop; + t.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(maxWorkers)); - blockPromise.Forget(); - Assert.LessOrEqual(activeWorkers, maxWorkers); + Assert.LessOrEqual(activeWorkers, maxWorkers); + } } [Test] @@ -281,14 +281,7 @@ public void SynchronizationContext_AllCodeExecutedOnCorrectContext_Async( .Finally(() => isComplete = true) .Forget(); - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return isComplete; - }, TimeSpan.FromSeconds(10))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => isComplete, TimeSpan.FromSeconds(10)); CollectionAssert.AreEqual(Enumerable.Range(1, 100), cq.OrderBy(i => i)); } @@ -365,39 +358,39 @@ public void Cancelation_SameTokenPassedToEveryInvocation_Async() public void Exceptions_HavePriorityOverCancelation_Async() { var deferred = Promise.NewDeferred(); - var promisePreserved = deferred.Promise.Preserve(); - var cts = CancelationSource.New(); - - Exception expected = new Exception(); - Exception actual = null; - - Promise.ParallelForEachAsync(Infinite(), (item, cancelationToken) => + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - if (item == 0) - { - return promisePreserved - .Then(() => - { - cts.Cancel(); - throw expected; - }); - } - else - { - deferred.TryResolve(); - return Promise.Resolved(); - } - }, cts.Token, maxDegreeOfParallelism: 2) - .Catch((Exception e) => actual = e) - .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(2)); + var cts = CancelationSource.New(); - promisePreserved.Forget(); - cts.Dispose(); + Exception expected = new Exception(); + Exception actual = null; - Assert.IsInstanceOf(actual); - var aggregate = (AggregateException) actual; - Assert.AreEqual(1, aggregate.InnerExceptions.Count); - Assert.AreEqual(expected, aggregate.InnerException); + Promise.ParallelForEachAsync(Infinite(), (item, cancelationToken) => + { + if (item == 0) + { + return promiseRetainer.WaitAsync() + .Then(() => + { + cts.Cancel(); + throw expected; + }); + } + else + { + deferred.TryResolve(); + return Promise.Resolved(); + } + }, cts.Token, maxDegreeOfParallelism: 2) + .Catch((Exception e) => actual = e) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(2)); + cts.Dispose(); + + Assert.IsInstanceOf(actual); + var aggregate = (AggregateException) actual; + Assert.AreEqual(1, aggregate.InnerExceptions.Count); + Assert.AreEqual(expected, aggregate.InnerException); + } } [Test] @@ -583,44 +576,42 @@ public void ParallelForEachAsync_CancelationCallbackExceptionsArePropagated( : TestHelper._backgroundContext; var deferred = Promise.NewDeferred(); - var blockPromise = deferred.Promise.Preserve(); - int readyCount = 0; - - var parallelPromise = Promise.ParallelForEachAsync(AsyncEnumerable.Range(0, 3), (index, cancelationToken) => + using (var blockPromiseRetainer = deferred.Promise.GetRetainer()) { - cancelationToken.Register(() => throw new Exception("Error in cancelation!")); - if (index == 2) + int readyCount = 0; + + var parallelPromise = Promise.ParallelForEachAsync(AsyncEnumerable.Range(0, 3), (index, cancelationToken) => { + cancelationToken.Register(() => throw new Exception("Error in cancelation!")); Interlocked.Increment(ref readyCount); - throw new System.InvalidOperationException("Error in loop body!"); - } - Interlocked.Increment(ref readyCount); - return blockPromise; - }, context); + if (index == 2) + { + // Wait until all iterations are ready, otherwise the token could be canceled before a worker registered, causing it to throw synchronously. + TestHelper.SpinUntil(() => readyCount == 3, TimeSpan.FromSeconds(2)); + throw new System.InvalidOperationException("Error in loop body!"); + } + return blockPromiseRetainer.WaitAsync(); + }, context); - SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return readyCount == 3; - }); + TestHelper.SpinUntilWhileExecutingForegroundContext(() => readyCount == 3, TimeSpan.FromSeconds(3)); - bool didThrow = false; - try - { - deferred.Resolve(); - parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); - } - catch (AggregateException e) - { - didThrow = true; - Assert.AreEqual(2, e.InnerExceptions.Count); - Assert.IsInstanceOf(e.InnerExceptions[0]); - Assert.IsInstanceOf(e.InnerExceptions[1]); - Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); - } + bool didThrow = false; + try + { + deferred.Resolve(); + parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(2, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.IsInstanceOf(e.InnerExceptions[1]); + Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); + } - Assert.True(didThrow); - blockPromise.Forget(); + Assert.True(didThrow); + } } #if UNITY_2021_2_OR_NEWER || !UNITY_2018_3_OR_NEWER diff --git a/Package/Tests/CoreTests/APIs/ParallelForTests.cs b/Package/Tests/CoreTests/APIs/ParallelForTests.cs index c0c5f5a9..2e16e4d7 100644 --- a/Package/Tests/CoreTests/APIs/ParallelForTests.cs +++ b/Package/Tests/CoreTests/APIs/ParallelForTests.cs @@ -95,23 +95,23 @@ public void Dop_WorkersCreatedRespectingLimit_Sync( int activeWorkers = 0; var block = Promise.NewDeferred(); - var blockPromise = block.Promise.Preserve(); - - Promise t = Promise.ParallelForEach(IterateUntilSet(box), (item, cancelationToken) => + using (var blockPromiseRetainer = block.Promise.GetRetainer()) { - Interlocked.Increment(ref activeWorkers); - return blockPromise; - }, maxDegreeOfParallelism: dop); + Promise t = Promise.ParallelForEach(IterateUntilSet(box), (item, cancelationToken) => + { + Interlocked.Increment(ref activeWorkers); + return blockPromiseRetainer.WaitAsync(); + }, maxDegreeOfParallelism: dop); - Thread.Sleep(20); // give the loop some time to run + Thread.Sleep(20); // give the loop some time to run - box.Value = true; - block.Resolve(); - int maxWorkers = dop == -1 ? Environment.ProcessorCount : dop; - t.WaitWithTimeout(TimeSpan.FromSeconds(maxWorkers)); + box.Value = true; + block.Resolve(); + int maxWorkers = dop == -1 ? Environment.ProcessorCount : dop; + t.WaitWithTimeout(TimeSpan.FromSeconds(maxWorkers)); - blockPromise.Forget(); - Assert.LessOrEqual(activeWorkers, maxWorkers); + Assert.LessOrEqual(activeWorkers, maxWorkers); + } } private static IEnumerable InfiniteZero() @@ -258,14 +258,7 @@ public void SynchronizationContext_AllCodeExecutedOnCorrectContext_Sync( .Finally(() => isComplete = true) .Forget(); - if (!SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return isComplete; - }, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntilWhileExecutingForegroundContext(() => isComplete, TimeSpan.FromSeconds(1)); CollectionAssert.AreEqual(Enumerable.Range(1, 100), cq.OrderBy(i => i)); } @@ -322,39 +315,40 @@ public void Cancelation_SameTokenPassedToEveryInvocation_Sync() public void Exceptions_HavePriorityOverCancelation_Sync() { var deferred = Promise.NewDeferred(); - var promisePreserved = deferred.Promise.Preserve(); - var cts = CancelationSource.New(); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + var cts = CancelationSource.New(); - Exception expected = new Exception(); - Exception actual = null; + Exception expected = new Exception(); + Exception actual = null; - Promise.ParallelForEach(Infinite(), (item, cancelationToken) => - { - if (item == 0) + Promise.ParallelForEach(Infinite(), (item, cancelationToken) => { - return promisePreserved - .Then(() => - { - cts.Cancel(); - throw expected; - }); - } - else - { - deferred.TryResolve(); - return Promise.Resolved(); - } - }, cts.Token, maxDegreeOfParallelism: 2) - .Catch((Exception e) => actual = e) - .WaitWithTimeout(TimeSpan.FromSeconds(2)); + if (item == 0) + { + return promiseRetainer.WaitAsync() + .Then(() => + { + cts.Cancel(); + throw expected; + }); + } + else + { + deferred.TryResolve(); + return Promise.Resolved(); + } + }, cts.Token, maxDegreeOfParallelism: 2) + .Catch((Exception e) => actual = e) + .WaitWithTimeout(TimeSpan.FromSeconds(2)); - promisePreserved.Forget(); - cts.Dispose(); + cts.Dispose(); - Assert.IsInstanceOf(actual); - var aggregate = (AggregateException) actual; - Assert.AreEqual(1, aggregate.InnerExceptions.Count); - Assert.AreEqual(expected, aggregate.InnerException); + Assert.IsInstanceOf(actual); + var aggregate = (AggregateException) actual; + Assert.AreEqual(1, aggregate.InnerExceptions.Count); + Assert.AreEqual(expected, aggregate.InnerException); + } } [Test] @@ -694,44 +688,42 @@ public void ParallelFor_CancelationCallbackExceptionsArePropagated( : TestHelper._backgroundContext; var deferred = Promise.NewDeferred(); - var blockPromise = deferred.Promise.Preserve(); - int readyCount = 0; - - var parallelPromise = Promise.ParallelFor(0, 3, (index, cancelationToken) => + using (var blockPromiseRetainer = deferred.Promise.GetRetainer()) { - cancelationToken.Register(() => throw new Exception("Error in cancelation!")); - if (index == 2) + int readyCount = 0; + + var parallelPromise = Promise.ParallelFor(0, 3, (index, cancelationToken) => { + cancelationToken.Register(() => throw new Exception("Error in cancelation!")); Interlocked.Increment(ref readyCount); - throw new System.InvalidOperationException("Error in loop body!"); - } - Interlocked.Increment(ref readyCount); - return blockPromise; - }, context); + if (index == 2) + { + // Wait until all iterations are ready, otherwise the token could be canceled before a worker registered, causing it to throw synchronously. + TestHelper.SpinUntil(() => readyCount == 3, TimeSpan.FromSeconds(2)); + throw new System.InvalidOperationException("Error in loop body!"); + } + return blockPromiseRetainer.WaitAsync(); + }, context); - SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return readyCount == 3; - }); + TestHelper.SpinUntilWhileExecutingForegroundContext(() => readyCount == 3, TimeSpan.FromSeconds(3)); - bool didThrow = false; - try - { - deferred.Resolve(); - parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); - } - catch (AggregateException e) - { - didThrow = true; - Assert.AreEqual(2, e.InnerExceptions.Count); - Assert.IsInstanceOf(e.InnerExceptions[0]); - Assert.IsInstanceOf(e.InnerExceptions[1]); - Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); - } + bool didThrow = false; + try + { + deferred.Resolve(); + parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(2, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.IsInstanceOf(e.InnerExceptions[1]); + Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); + } - Assert.True(didThrow); - blockPromise.Forget(); + Assert.True(didThrow); + } } [Test] @@ -743,44 +735,42 @@ public void ParallelForEach_CancelationCallbackExceptionsArePropagated( : TestHelper._backgroundContext; var deferred = Promise.NewDeferred(); - var blockPromise = deferred.Promise.Preserve(); - int readyCount = 0; - - var parallelPromise = Promise.ParallelForEach(Enumerable.Range(0, 3), (index, cancelationToken) => + using (var blockPromiseRetainer = deferred.Promise.GetRetainer()) { - cancelationToken.Register(() => throw new Exception("Error in cancelation!")); - if (index == 2) + int readyCount = 0; + + var parallelPromise = Promise.ParallelForEach(Enumerable.Range(0, 3), (index, cancelationToken) => { + cancelationToken.Register(() => throw new Exception("Error in cancelation!")); Interlocked.Increment(ref readyCount); - throw new System.InvalidOperationException("Error in loop body!"); - } - Interlocked.Increment(ref readyCount); - return blockPromise; - }, context); + if (index == 2) + { + // Wait until all iterations are ready, otherwise the token could be canceled before a worker registered, causing it to throw synchronously. + TestHelper.SpinUntil(() => readyCount == 3, TimeSpan.FromSeconds(2)); + throw new System.InvalidOperationException("Error in loop body!"); + } + return blockPromiseRetainer.WaitAsync(); + }, context); - SpinWait.SpinUntil(() => - { - TestHelper.ExecuteForegroundCallbacks(); - return readyCount == 3; - }); + TestHelper.SpinUntilWhileExecutingForegroundContext(() => readyCount == 3, TimeSpan.FromSeconds(3)); - bool didThrow = false; - try - { - deferred.Resolve(); - parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); - } - catch (AggregateException e) - { - didThrow = true; - Assert.AreEqual(2, e.InnerExceptions.Count); - Assert.IsInstanceOf(e.InnerExceptions[0]); - Assert.IsInstanceOf(e.InnerExceptions[1]); - Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); - } + bool didThrow = false; + try + { + deferred.Resolve(); + parallelPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(Environment.ProcessorCount)); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(2, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.IsInstanceOf(e.InnerExceptions[1]); + Assert.AreEqual(3, ((AggregateException) e.InnerExceptions[1]).InnerExceptions.Count); + } - Assert.True(didThrow); - blockPromise.Forget(); + Assert.True(didThrow); + } } } #endif // !UNITY_WEBGL diff --git a/Package/Tests/CoreTests/APIs/PromiseCancelationTests.cs b/Package/Tests/CoreTests/APIs/PromiseCancelationTests.cs index 8ace3d13..c112a603 100644 --- a/Package/Tests/CoreTests/APIs/PromiseCancelationTests.cs +++ b/Package/Tests/CoreTests/APIs/PromiseCancelationTests.cs @@ -7,6 +7,7 @@ using NUnit.Framework; using Proto.Promises; using System; +using System.Collections.Generic; namespace ProtoPromiseTests.APIs { @@ -434,29 +435,25 @@ public void Teardown() public void ItMustBeCalledAfterPromiseIsCanceled() { var canceled = false; - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - - TestHelper.AddCancelCallbacks(promise, - onCancel: () => - { - canceled = true; - } - ); - TestHelper.AddCancelCallbacks(promise, - onCancel: () => - { - canceled = true; - } - ); - cancelationSource.Cancel(); - - Assert.True(canceled); + using (var promiseRetainer = deferred.Promise.GetRetainer()) + { + TestHelper.AddCancelCallbacks(promiseRetainer.WaitAsync(), + onCancel: () => + { + canceled = true; + } + ); + TestHelper.AddCancelCallbacks(promiseRetainer.WaitAsync(), + onCancel: () => + { + canceled = true; + } + ); - cancelationSource.Dispose(); - promise.Forget(); + deferred.Cancel(); + Assert.True(canceled); + } } [Test] @@ -607,55 +604,45 @@ public void Teardown() } [Test] - public void IfWhenPromiseCancelationIsCanceled_AllRespectiveOnCanceledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToCatchCancelation_void() + public void IfWhenPromiseCanceled_AllRespectiveOnCanceledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToCatchCancelation_void() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int index = i; - promise - .CatchCancelation(() => Assert.AreEqual(index, counter++)) - .Forget(); - } - - cancelationSource.Cancel(); + int counter = 0; - Assert.AreEqual(10, counter); + for (int i = 0; i < 10; ++i) + { + int index = i; + promiseRetainer.WaitAsync() + .CatchCancelation(() => Assert.AreEqual(index, counter++)) + .Forget(); + } - cancelationSource.Dispose(); - promise.Forget(); + deferred.Cancel(); + Assert.AreEqual(10, counter); + } } [Test] - public void IfWhenPromiseCancelationIsCanceled_AllRespectiveOnCanceledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToCatchCancelation_T() + public void IfWhenPromiseIsCanceled_AllRespectiveOnCanceledCallbacksMustExecuteInTheOrderOfTheirOriginatingCallsToCatchCancelation_T() { - CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - cancelationSource.Token.Register(deferred); - var promise = deferred.Promise.Preserve(); - - int counter = 0; - - for (int i = 0; i < 10; ++i) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - int index = i; - promise - .CatchCancelation(() => Assert.AreEqual(index, counter++)) - .Forget(); - } - - cancelationSource.Cancel(); + int counter = 0; - Assert.AreEqual(10, counter); + for (int i = 0; i < 10; ++i) + { + int index = i; + promiseRetainer.WaitAsync() + .CatchCancelation(() => Assert.AreEqual(index, counter++)) + .Forget(); + } - cancelationSource.Dispose(); - promise.Forget(); + deferred.Cancel(); + Assert.AreEqual(10, counter); + } } } @@ -871,105 +858,75 @@ public void Teardown() [Test] public void IfPromiseAndXReferToTheSameObject_RejectPromiseWithInvalidReturnExceptionAsTheReason_void() { + string expectedMessage = "A Promise cannot wait on itself."; int exceptionCounter = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestAction catchCallback = (ref Promise p) => + // When the promise awaits itself, it becomes rejected and invalidated, so the InvalidReturnException gets sent to the UncaughtRejectionHandler. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + Promise.Config.UncaughtRejectionHandler = e => { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); - }; - TestAction> catchCallbackConvert = (ref Promise p) => - { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); + Assert.IsAssignableFrom(e.Value); + Assert.AreEqual(expectedMessage, e.Value.UnsafeAs().Message); + ++exceptionCounter; }; - TestHelper.AddCancelCallbacks(promise, - promiseToPromise: p => p, - onCallbackAdded: catchCallback - ); - TestHelper.AddContinueCallbacks(promise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert + var deferred = Promise.NewDeferred(); + var promiseQueue = new Queue(); + + TestHelper.AddCancelCallbacks(deferred.Promise, + onAdoptCallbackAdded: (ref Promise p) => + { + promiseQueue.Enqueue(p); + p = Promise.Resolved(); + }, + promiseToPromise: p => promiseQueue.Dequeue() ); deferred.Cancel(); Assert.AreEqual( - (TestHelper.onCancelCallbacks * 2) + TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks, + TestHelper.onCancelPromiseCallbacks * 2, exceptionCounter ); - promise.Forget(); + Promise.Config.UncaughtRejectionHandler = currentHandler; } [Test] public void IfPromiseAndXReferToTheSameObject_RejectPromiseWithInvalidReturnExceptionAsTheReason_T() { + string expectedMessage = "A Promise cannot wait on itself."; int exceptionCounter = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - TestAction catchCallback = (ref Promise p) => - { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); - }; - TestAction> catchCallbackConvert = (ref Promise p) => + // When the promise awaits itself, it becomes rejected and invalidated, so the InvalidReturnException gets sent to the UncaughtRejectionHandler. + var currentHandler = Promise.Config.UncaughtRejectionHandler; + Promise.Config.UncaughtRejectionHandler = e => { - var preserved = p.Preserve(); - p = preserved; - p.Finally(() => preserved.Forget()) - .Catch((object e) => - { - Assert.IsInstanceOf(e); - ++exceptionCounter; - }).Forget(); + Assert.IsAssignableFrom(e.Value); + Assert.AreEqual(expectedMessage, e.Value.UnsafeAs().Message); + ++exceptionCounter; }; - TestHelper.AddCancelCallbacks(promise, - promiseToPromise: p => p, - onCallbackAdded: catchCallbackConvert - ); - TestHelper.AddContinueCallbacks(promise, - promiseToPromise: p => p, - promiseToPromiseConvert: p => p, - onCallbackAdded: catchCallback, - onCallbackAddedConvert: catchCallbackConvert + var deferred = Promise.NewDeferred(); + var promiseQueue = new Queue>(); + + TestHelper.AddCancelCallbacks(deferred.Promise, + onAdoptCallbackAdded: (ref Promise p) => + { + promiseQueue.Enqueue(p); + p = Promise.Resolved(1); + }, + promiseToPromise: p => promiseQueue.Dequeue() ); deferred.Cancel(); Assert.AreEqual( - (TestHelper.onCancelCallbacks * 2) + TestHelper.continueVoidPromiseVoidCallbacks + TestHelper.continueVoidPromiseConvertCallbacks, + TestHelper.onCancelPromiseCallbacks * 2, exceptionCounter ); - promise.Forget(); + Promise.Config.UncaughtRejectionHandler = currentHandler; } #endif @@ -994,15 +951,15 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa int completeCounter = 0; var cancelDeferred = Promise.NewDeferred(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); var resolveWaitDeferred = Promise.NewDeferred(); var rejectWaitDeferred = Promise.NewDeferred(); var cancelWaitDeferred = Promise.NewDeferred(); - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var cancelWaitPromise = cancelWaitDeferred.Promise.Preserve(); + var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer(); + var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer(); + var cancelWaitPromiseRetainer = cancelWaitDeferred.Promise.GetRetainer(); TestAction onAdoptCallbackAdded = (ref Promise p) => { @@ -1010,16 +967,16 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa .Catch(() => { }); }; - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => resolveWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => resolveWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => rejectWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => rejectWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => cancelWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => cancelWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1038,10 +995,10 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa expectedCompleteCount += TestHelper.onCancelCallbacks; Assert.AreEqual(expectedCompleteCount, completeCounter); - cancelPromise.Forget(); - resolveWaitPromise.Forget(); - rejectWaitPromise.Forget(); - cancelWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + resolveWaitPromiseRetainer.Dispose(); + rejectWaitPromiseRetainer.Dispose(); + cancelWaitPromiseRetainer.Dispose(); } [Test] @@ -1051,15 +1008,15 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa int completeCounter = 0; var cancelDeferred = Promise.NewDeferred(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); var resolveWaitDeferred = Promise.NewDeferred(); var rejectWaitDeferred = Promise.NewDeferred(); var cancelWaitDeferred = Promise.NewDeferred(); - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); - var cancelWaitPromise = cancelWaitDeferred.Promise.Preserve(); + var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer(); + var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer(); + var cancelWaitPromiseRetainer = cancelWaitDeferred.Promise.GetRetainer(); TestAction> onAdoptCallbackAdded = (ref Promise p) => { @@ -1067,16 +1024,16 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa .Catch(() => 1); }; - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => resolveWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => resolveWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => rejectWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => rejectWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); - TestHelper.AddCancelCallbacks(cancelPromise, - promiseToPromise: p => cancelWaitPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), + promiseToPromise: p => cancelWaitPromiseRetainer.WaitAsync(), onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1095,10 +1052,10 @@ public void IfXIsPending_PromiseMustRemainPendingUntilXIsFulfilledOrRejectedOrCa expectedCompleteCount += TestHelper.onCancelCallbacks; Assert.AreEqual(expectedCompleteCount, completeCounter); - cancelPromise.Forget(); - resolveWaitPromise.Forget(); - rejectWaitPromise.Forget(); - cancelWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + resolveWaitPromiseRetainer.Dispose(); + rejectWaitPromiseRetainer.Dispose(); + cancelWaitPromiseRetainer.Dispose(); } [Test] @@ -1107,7 +1064,7 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_void() var cancelDeferred = Promise.NewDeferred(); cancelDeferred.Cancel(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); int resolveCounter = 0; @@ -1120,16 +1077,16 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_void() }; var resolveWaitDeferred = Promise.NewDeferred(); - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); + var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer(); - Func promiseToPromise = p => resolveWaitPromise; + Func promiseToPromise = p => resolveWaitPromiseRetainer.WaitAsync(); // Test pending -> resolved and already resolved. bool firstRun = true; RunAgain: resolveCounter = 0; - TestHelper.AddCancelCallbacks(cancelPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), promiseToPromise: promiseToPromise, onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1148,8 +1105,8 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_void() goto RunAgain; } - cancelPromise.Forget(); - resolveWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + resolveWaitPromiseRetainer.Dispose(); } [Test] @@ -1158,7 +1115,7 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_T() var cancelDeferred = Promise.NewDeferred(); cancelDeferred.Cancel(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); int resolveValue = 100; int resolveCounter = 0; @@ -1174,16 +1131,16 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_T() }; var resolveWaitDeferred = Promise.NewDeferred(); - var resolveWaitPromise = resolveWaitDeferred.Promise.Preserve(); + var resolveWaitPromiseRetainer = resolveWaitDeferred.Promise.GetRetainer(); - Func, Promise> promiseToPromise = p => resolveWaitPromise; + Func, Promise> promiseToPromise = p => resolveWaitPromiseRetainer.WaitAsync(); // Test pending -> resolved and already resolved. bool firstRun = true; RunAgain: resolveCounter = 0; - TestHelper.AddCancelCallbacks(cancelPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), promiseToPromise: promiseToPromise, onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1202,8 +1159,8 @@ public void IfWhenXIsFulfilled_FulfillPromiseWithTheSameValue_T() goto RunAgain; } - cancelPromise.Forget(); - resolveWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + resolveWaitPromiseRetainer.Dispose(); } [Test] @@ -1212,7 +1169,7 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_void() var cancelDeferred = Promise.NewDeferred(); cancelDeferred.Cancel(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); float rejectReason = 1.5f; int rejectCounter = 0; @@ -1227,16 +1184,16 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_void() }; var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); + var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer(); - Func promiseToPromise = p => rejectWaitPromise; + Func promiseToPromise = p => rejectWaitPromiseRetainer.WaitAsync(); // Test pending -> rejected and already rejected. bool firstRun = true; RunAgain: rejectCounter = 0; - TestHelper.AddCancelCallbacks(cancelPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), promiseToPromise: promiseToPromise, onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1255,8 +1212,8 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_void() goto RunAgain; } - cancelPromise.Forget(); - rejectWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + rejectWaitPromiseRetainer.Dispose(); } [Test] @@ -1265,7 +1222,7 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_T() var cancelDeferred = Promise.NewDeferred(); cancelDeferred.Cancel(); - var cancelPromise = cancelDeferred.Promise.Preserve(); + var cancelPromiseRetainer = cancelDeferred.Promise.GetRetainer(); float rejectReason = 1.5f; int rejectCounter = 0; @@ -1281,16 +1238,16 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_T() }; var rejectWaitDeferred = Promise.NewDeferred(); - var rejectWaitPromise = rejectWaitDeferred.Promise.Preserve(); + var rejectWaitPromiseRetainer = rejectWaitDeferred.Promise.GetRetainer(); - Func, Promise> promiseToPromise = p => rejectWaitPromise; + Func, Promise> promiseToPromise = p => rejectWaitPromiseRetainer.WaitAsync(); // Test pending -> rejected and already rejected. bool firstRun = true; RunAgain: rejectCounter = 0; - TestHelper.AddCancelCallbacks(cancelPromise, + TestHelper.AddCancelCallbacks(cancelPromiseRetainer.WaitAsync(), promiseToPromise: promiseToPromise, onAdoptCallbackAdded: onAdoptCallbackAdded ); @@ -1309,8 +1266,8 @@ public void IfWhenXIsRejected_RejectPromiseWithTheSameReason_T() goto RunAgain; } - cancelPromise.Forget(); - rejectWaitPromise.Forget(); + cancelPromiseRetainer.Dispose(); + rejectWaitPromiseRetainer.Dispose(); } } @@ -1375,6 +1332,7 @@ public void IfOnResolvedOrOnRejectedReturnsSuccessfully_ResolvePromise_T() [Test] public void IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_void() { + string expectedMessage = "Circular Promise chain detected."; var deferred = Promise.NewDeferred(); int exceptionCounter = 0; @@ -1382,26 +1340,33 @@ public void IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithIn Action catcher = (object o) => { Assert.IsInstanceOf(o); + Assert.AreEqual(expectedMessage, o.UnsafeAs().Message); ++exceptionCounter; }; Func promiseToPromise = p => { - p.Catch(catcher).Forget(); - return p.ThenDuplicate().ThenDuplicate().Catch(() => { }); + using (var promiseRetainer = p.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => { }); + } }; + bool adopted = false; + TestAction onAdoptCallbackAdded = (ref Promise p) => adopted = true; TestAction onCallbackAdded = (ref Promise p) => { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); + if (!adopted) + { + p.Forget(); + } + adopted = false; }; TestHelper.AddCancelCallbacks(deferred.Promise, promiseToPromise: promiseToPromise, + onAdoptCallbackAdded: onAdoptCallbackAdded, onCallbackAdded: onCallbackAdded ); @@ -1413,6 +1378,7 @@ public void IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithIn [Test] public void IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithInvalidReturnExceptionAsTheReason_T() { + string expectedMessage = "Circular Promise chain detected."; var deferred = Promise.NewDeferred(); int exceptionCounter = 0; @@ -1420,26 +1386,33 @@ public void IfXIsAPromiseAndItResultsInACircularPromiseChain_RejectPromiseWithIn Action catcher = (object o) => { Assert.IsInstanceOf(o); + Assert.AreEqual(expectedMessage, o.UnsafeAs().Message); ++exceptionCounter; }; Func, Promise> promiseToPromise = p => { - p.Catch(catcher).Forget(); - return p.ThenDuplicate().ThenDuplicate().Catch(() => 1); + using (var promiseRetainer = p.GetRetainer()) + { + promiseRetainer.WaitAsync().Catch(catcher).Forget(); + return promiseRetainer.WaitAsync().ThenDuplicate().ThenDuplicate().Catch(() => 1); + } }; + bool adopted = false; + TestAction> onAdoptCallbackAdded = (ref Promise p) => adopted = true; TestAction> onCallbackAdded = (ref Promise p) => { - var preserved = p = p.Preserve(); - preserved - .Catch(() => { }) - .Finally(() => preserved.Forget()) - .Forget(); + if (!adopted) + { + p.Forget(); + } + adopted = false; }; TestHelper.AddCancelCallbacks(deferred.Promise, promiseToPromise: promiseToPromise, + onAdoptCallbackAdded: onAdoptCallbackAdded, onCallbackAdded: onCallbackAdded ); @@ -1581,13 +1554,13 @@ public void CatchCancelationCaptureValue() CancelationSource cancelationSource = CancelationSource.New(); int captureValue = 100; var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); - promise + promiseRetainer.WaitAsync() .Then(() => { }, cancelationSource.Token) .CatchCancelation(captureValue, cv => Assert.AreEqual(captureValue, cv)) .Forget(); - promise + promiseRetainer.WaitAsync() .Then(() => 1f, cancelationSource.Token) .CatchCancelation(captureValue, cv => Assert.AreEqual(captureValue, cv)) .Forget(); @@ -1596,7 +1569,7 @@ public void CatchCancelationCaptureValue() deferred.Resolve(); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } public class CancelationToken @@ -1833,16 +1806,16 @@ public void OnResolvedIsNotInvokedIfTokenIsCanceled_void() { CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token @@ -1852,7 +1825,7 @@ public void OnResolvedIsNotInvokedIfTokenIsCanceled_void() deferred.Resolve(); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -1860,16 +1833,16 @@ public void OnResolvedIsNotInvokedIfTokenIsCanceled_T() { CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token @@ -1879,7 +1852,7 @@ public void OnResolvedIsNotInvokedIfTokenIsCanceled_T() deferred.Resolve(1); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -1889,16 +1862,16 @@ public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_void0() cancelationSource.Cancel(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token @@ -1907,31 +1880,30 @@ public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_void0() deferred.Resolve(); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_void1() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: Proto.Promises.CancelationToken.Canceled() ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: () => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: Proto.Promises.CancelationToken.Canceled() ); deferred.Resolve(); - - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -1941,16 +1913,16 @@ public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_T0() cancelationSource.Cancel(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: cancelationSource.Token @@ -1959,31 +1931,30 @@ public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_T0() deferred.Resolve(1); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] public void OnResolvedIsNotInvokedIfTokenIsAlreadyCanceled_T1() { var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: Proto.Promises.CancelationToken.Canceled() ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), onResolve: _ => Assert.Fail("OnResolved was invoked."), onResolveCapture: _ => Assert.Fail("OnResolved was invoked."), cancelationToken: Proto.Promises.CancelationToken.Canceled() ); deferred.Resolve(1); - - promise.Forget(); + promiseRetainer.Dispose(); } public class Reject @@ -2248,22 +2219,22 @@ public void PromiseIsCanceledFromToken_void() CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); int cancelCallbacks = 0; TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddContinueCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); @@ -2277,7 +2248,7 @@ public void PromiseIsCanceledFromToken_void() ); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -2286,22 +2257,22 @@ public void PromiseIsCanceledFromToken_T() CancelationSource cancelationSource = CancelationSource.New(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); int cancelCallbacks = 0; TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddContinueCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); @@ -2315,7 +2286,7 @@ public void PromiseIsCanceledFromToken_T() ); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -2325,22 +2296,22 @@ public void PromiseIsCanceledFromAlreadyCanceledToken_void() cancelationSource.Cancel(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); int cancelCallbacks = 0; TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddContinueCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); @@ -2353,7 +2324,7 @@ public void PromiseIsCanceledFromAlreadyCanceledToken_void() ); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } [Test] @@ -2363,22 +2334,22 @@ public void PromiseIsCanceledFromAlreadyCanceledToken_T() cancelationSource.Cancel(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); int cancelCallbacks = 0; TestHelper.AddResolveCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); TestHelper.AddContinueCallbacksWithCancelation( - promise, + promiseRetainer.WaitAsync(), cancelationToken: cancelationSource.Token, onCancel: () => ++cancelCallbacks ); @@ -2391,7 +2362,7 @@ public void PromiseIsCanceledFromAlreadyCanceledToken_T() ); cancelationSource.Dispose(); - promise.Forget(); + promiseRetainer.Dispose(); } } } diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups.meta b/Package/Tests/CoreTests/APIs/PromiseGroups.meta new file mode 100644 index 00000000..26d73676 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 40ce278391cb4884dad3b7168d2b8407 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs new file mode 100644 index 00000000..9f9ae83e --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs @@ -0,0 +1,417 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseAllGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseAllGroup_UsingInvalidatedGroupThrows( + [Values] CancelationType cancelationType, + [Values] bool provideList) + { + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseAllGroup).Add(intPromise)); + Assert.Catch(() => default(PromiseAllGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup1 = cancelationType == CancelationType.None ? PromiseAllGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllGroup.New(cancelationSource.Token, out _, list) + : PromiseAllGroup.New(CancelationToken.Canceled(), out _, list); + + var allGroup2 = allGroup1.Add(Promise.Resolved(2)); + Assert.Catch(() => allGroup1.Add(intPromise)); + Assert.Catch(() => allGroup1.WaitAsync()); + + var allGroup3 = allGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => allGroup2.Add(intPromise)); + Assert.Catch(() => allGroup2.WaitAsync()); + + allGroup3.WaitAsync().Forget(); + Assert.Catch(() => allGroup3.Add(intPromise)); + Assert.Catch(() => allGroup3.WaitAsync()); + + intPromise.Forget(); + } + } + + [Test] + public void PromiseAllGroupIsResolvedWhenNoPromisesAreAdded( + [Values] CancelationType cancelationType, + [Values] bool provideList) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllGroup.New(cancelationSource.Token, out _, list) + : PromiseAllGroup.New(CancelationToken.Canceled(), out _, list); + + bool resolved = false; + + allGroup + .WaitAsync() + .Then(values => + { + resolved = true; + Assert.Zero(values.Count); + if (provideList) + { + Assert.AreSame(list, values); + } + }) + .Forget(); + + Assert.True(resolved); + } + } + + [Test] + public void PromiseAllGroupIsCompletedWhenAllPromisesAreCompleted_1( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllGroup.New(cancelationSource.Token, out _, list) + : PromiseAllGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, value1, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual(completeType, (CompleteType) result.State); + if (completeType == CompleteType.Reject) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType == CompleteType.Resolve) + { + CollectionAssert.AreEqual(new[] { value1 }, result.Value); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllGroupIsCompletedWhenAllPromisesAreCompleted_2( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllGroup.New(cancelationSource.Token, out _, list) + : PromiseAllGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + int value2 = 2; + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + CollectionAssert.AreEqual(new[] { value1, value2 }, result.Value); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + private static Promise.State GetExpectedState(CancelationType cancelationType, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + if (cancelationType == CancelationType.Immediate) + { + return Promise.State.Canceled; + } + + bool allAlreadyComplete = true; + foreach (var cv in completeValues) + { + if (cv.alreadyComplete) + { + if (cv.completeType != CompleteType.Resolve) + { + return (Promise.State) cv.completeType; + } + } + else + { + allAlreadyComplete = false; + } + } + if (allAlreadyComplete) + { + return Promise.State.Resolved; + } + + if (cancelationType == CancelationType.None) + { + foreach (var cv in completeValues) + { + if (cv.completeType != CompleteType.Resolve) + { + return (Promise.State) cv.completeType; + } + } + return Promise.State.Resolved; + } + + // CancelationType.Deferred - cancelation source is canceled after the first promise is completed. + if (completeValues[0].completeType != CompleteType.Resolve) + { + return (Promise.State) completeValues[0].completeType; + } + if (completeValues.Skip(1).All(cv => cv.alreadyComplete && cv.completeType == CompleteType.Resolve)) + { + return Promise.State.Resolved; + } + return Promise.State.Canceled; + } + + private static bool GetShouldBeComplete(int indexOfCompletion, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + // indexOfCompletion = 0 means none explicitly completed yet. + for (int i = 0; i < indexOfCompletion; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + bool allAlreadyComplete = true; + for (int i = indexOfCompletion; i < completeValues.Length; ++i) + { + if (!completeValues[i].alreadyComplete) + { + allAlreadyComplete = false; + continue; + } + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + return allAlreadyComplete; + } + + [Test] + public void PromiseAllGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_2( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllGroup.New(out var groupCancelationToken, list) + : cancelationType == CancelationType.Deferred ? PromiseAllGroup.New(cancelationSource.Token, out groupCancelationToken, list) + : PromiseAllGroup.New(CancelationToken.Canceled(), out groupCancelationToken, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + int value2 = 2; + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + CollectionAssert.AreEqual(new[] { value1, value2 }, result.Value); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllGroup_CancelationCallbackExceptionsArePropagated_2( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool provideList, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllGroup.New(out var groupCancelationToken, list) + : PromiseAllGroup.New(cancelationSource.Token, out groupCancelationToken, list); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + if (expectedExceptionCount > 1) + { + Assert.IsInstanceOf(e.InnerExceptions[1]); + } + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs.meta new file mode 100644 index 00000000..51e57672 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d0ad1b2151844fc43b6d30bd4a157271 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs new file mode 100644 index 00000000..e6bfe2b1 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs @@ -0,0 +1,660 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseAllResultsGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + private static void AssertResult(CompleteType completeType, Promise.ResultContainer resultContainer, TReject expectedReason) + { + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedReason, resultContainer.Reason); + } + } + + private static void AssertResult(CompleteType completeType, Promise.ResultContainer resultContainer, TResult expectedResult, TReject expectedReason) + { + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(expectedResult, resultContainer.Value); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedReason, resultContainer.Reason); + } + } + + [Test] + public void PromiseAllResultsGroup_UsingInvalidatedGroupThrows_void( + [Values] CancelationType cancelationType) + { + var voidPromise = Promise.Resolved(); + Assert.Catch(() => default(PromiseAllResultsGroup).Add(voidPromise)); + + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup1 = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _); + + var mergeGroup2 = mergeGroup1.Add(Promise.Resolved()); + Assert.Catch(() => mergeGroup1.Add(voidPromise)); + Assert.Catch(() => mergeGroup1.WaitAsync()); + + var mergeGroup3 = mergeGroup2.Add(Promise.Resolved()); + Assert.Catch(() => mergeGroup2.Add(voidPromise)); + Assert.Catch(() => mergeGroup2.WaitAsync()); + + mergeGroup3.WaitAsync().Forget(); + Assert.Catch(() => mergeGroup3.Add(voidPromise)); + Assert.Catch(() => mergeGroup3.WaitAsync()); + + voidPromise.Forget(); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenNoPromisesAreAdded_void( + [Values] CancelationType cancelationType, + [Values] bool provideList) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + bool resolved = false; + + allGroup + .WaitAsync() + .Then(values => + { + resolved = true; + Assert.Zero(values.Count); + if (provideList) + { + Assert.AreSame(list, values); + } + }) + .Forget(); + + Assert.True(resolved); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_1_void( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(1, result.Value.Count); + AssertResult(completeType, result.Value[0], expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_2_void( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(2, result.Value.Count); + AssertResult(completeType1, result.Value[0], expectedException); + AssertResult(completeType2, result.Value[1], expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + private static bool GetShouldBeComplete(int indexOfCompletion, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + // indexOfCompletion = 0 means none explicitly completed yet. + for (int i = 0; i < indexOfCompletion; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + bool allAlreadyComplete = true; + for (int i = indexOfCompletion; i < completeValues.Length; ++i) + { + if (!completeValues[i].alreadyComplete) + { + allAlreadyComplete = false; + continue; + } + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + return allAlreadyComplete; + } + + private static CompleteType GetExpectedState(int index, CancelationType cancelationType, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + if (cancelationType == CancelationType.Immediate) + { + return CompleteType.Cancel; + } + + for (int i = 0; i < index; ++i) + { + var cv = completeValues[i]; + if (cv.alreadyComplete && cv.completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + if (completeValues[index].alreadyComplete) + { + return completeValues[index].completeType; + } + + for (int i = index + 1; i < completeValues.Length; ++i) + { + var cv = completeValues[i]; + if (cv.alreadyComplete && cv.completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + if (cancelationType == CancelationType.Deferred) + { + // Cancelation source is canceled after the first promise is completed. + if (index == 0) + { + return completeValues[0].completeType; + } + + return CompleteType.Cancel; + } + + // CancelationType.None + + for (int i = 0; i < index; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + return completeValues[index].completeType; + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_2_void( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out var groupCancelationToken, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out groupCancelationToken, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken, list); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(2, result.Value.Count); + AssertResult(GetExpectedState(0, cancelationType, completeValues), result.Value[0], expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result.Value[1], expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroup_CancelationCallbackExceptionsArePropagated_2_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool provideList, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out var groupCancelationToken, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out groupCancelationToken, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken, list); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroup_UsingInvalidatedGroupThrows_T( + [Values] CancelationType cancelationType) + { + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseAllResultsGroup).Add(intPromise)); + + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup1 = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _); + + var mergeGroup2 = mergeGroup1.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup1.Add(intPromise)); + Assert.Catch(() => mergeGroup1.WaitAsync()); + + var mergeGroup3 = mergeGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup2.Add(intPromise)); + Assert.Catch(() => mergeGroup2.WaitAsync()); + + mergeGroup3.WaitAsync().Forget(); + Assert.Catch(() => mergeGroup3.Add(intPromise)); + Assert.Catch(() => mergeGroup3.WaitAsync()); + + intPromise.Forget(); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenNoPromisesAreAdded_T( + [Values] CancelationType cancelationType, + [Values] bool provideList) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List.ResultContainer>() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + bool resolved = false; + + allGroup + .WaitAsync() + .Then(values => + { + resolved = true; + Assert.Zero(values.Count); + if (provideList) + { + Assert.AreSame(list, values); + } + }) + .Forget(); + + Assert.True(resolved); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_1_T( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List.ResultContainer>() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, value1, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(1, result.Value.Count); + AssertResult(completeType, result.Value[0], value1, expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_2_T( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List.ResultContainer>() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out _, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out _, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out _, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + int value2 = 2; + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(2, result.Value.Count); + AssertResult(completeType1, result.Value[0], value1, expectedException); + AssertResult(completeType2, result.Value[1], value2, expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_2_T( + [Values] CancelationType cancelationType, + [Values] bool provideList, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List.ResultContainer>() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out var groupCancelationToken, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out groupCancelationToken, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken, list); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + int value2 = 2; + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(2, result.Value.Count); + AssertResult(GetExpectedState(0, cancelationType, completeValues), result.Value[0], value1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result.Value[1], value2, expectedException); + if (provideList) + { + Assert.AreSame(list, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseAllResultsGroup_CancelationCallbackExceptionsArePropagated_2_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool provideList, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var list = provideList ? new List.ResultContainer>() : null; + var allGroup = cancelationType == CancelationType.None ? PromiseAllResultsGroup.New(out var groupCancelationToken, list) + : cancelationType == CancelationType.Deferred ? PromiseAllResultsGroup.New(cancelationSource.Token, out groupCancelationToken, list) + : PromiseAllResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken, list); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + allGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs.meta new file mode 100644 index 00000000..d8e4cc23 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseAllResultsGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: c53555bc7dd0efa41b4b1300c3b22f06 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs new file mode 100644 index 00000000..b7c55e64 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs @@ -0,0 +1,1603 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using System.Linq; +using Proto.Promises; +using NUnit.Framework; +using System; +using System.Collections.Generic; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseEachGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseEachGroup_UsingInvalidatedGroupThrows_void( + [Values] CancelationType cancelationType, + [Values] bool suppressUnobservedRejections) + { + var voidPromise = Promise.Resolved(); + Assert.Catch(() => default(PromiseEachGroup).Add(voidPromise)); + Assert.Catch(() => default(PromiseEachGroup).GetAsyncEnumerable()); + + using (var cancelationSource = CancelationSource.New()) + { + var eachGroup1 = cancelationType == CancelationType.None ? PromiseEachGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseEachGroup.New(cancelationSource.Token, out _) + : PromiseEachGroup.New(CancelationToken.Canceled(), out _); + + var eachGroup2 = eachGroup1.Add(Promise.Resolved()); + Assert.Catch(() => eachGroup1.Add(voidPromise)); + Assert.Catch(() => eachGroup1.GetAsyncEnumerable()); + + var eachGroup3 = eachGroup2.Add(Promise.Resolved()); + Assert.Catch(() => eachGroup2.Add(voidPromise)); + Assert.Catch(() => eachGroup2.GetAsyncEnumerable()); + + eachGroup3.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator().DisposeAsync().Forget(); + Assert.Catch(() => eachGroup3.Add(voidPromise)); + Assert.Catch(() => eachGroup3.GetAsyncEnumerable()); + + voidPromise.Forget(); + } + } + + [Test] + public void PromiseEachGroup_UsingInvalidatedGroupThrows_T( + [Values] CancelationType cancelationType, + [Values] bool suppressUnobservedRejections) + { + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseEachGroup).Add(intPromise)); + Assert.Catch(() => default(PromiseEachGroup).GetAsyncEnumerable()); + + using (var cancelationSource = CancelationSource.New()) + { + var eachGroup1 = cancelationType == CancelationType.None ? PromiseEachGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseEachGroup.New(cancelationSource.Token, out _) + : PromiseEachGroup.New(CancelationToken.Canceled(), out _); + + var eachGroup2 = eachGroup1.Add(Promise.Resolved(2)); + Assert.Catch(() => eachGroup1.Add(intPromise)); + Assert.Catch(() => eachGroup1.GetAsyncEnumerable()); + + var eachGroup3 = eachGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => eachGroup2.Add(intPromise)); + Assert.Catch(() => eachGroup2.GetAsyncEnumerable()); + + eachGroup3.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator().DisposeAsync().Forget(); + Assert.Catch(() => eachGroup3.Add(intPromise)); + Assert.Catch(() => eachGroup3.GetAsyncEnumerable()); + + intPromise.Forget(); + } + } + + private static IEnumerable GetArgs() => EachTests.GetArgs(); + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseEachGroupResultsAreYieldedInCompletionOrder_void((CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] args) + { + Promise.Run(async () => + { + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupResultsAreYieldedInCompletionOrder_MoveNextAsyncBeforeComplete_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, false, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + bool movedNext = false; + var moveNextPromise = asyncEnumerator.MoveNextAsync() + .Finally(() => movedNext = true); + Assert.False(movedNext); + tryCompleters[completeIndex].Invoke(); + Assert.True(await moveNextPromise); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupDisposeEarly_void( + [Values] bool suppressUnobservedRejections) + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + Assert.True(runComplete); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupDisposeWithoutMoveNext_void( + [Values] bool suppressUnobservedRejections) + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + bool didThrow = false; + try + { + await eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator().DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + foreach (var (_, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + } + Assert.True(runComplete); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupCancelIterationEarly_void( + [Values] bool disposeCancelationSourceEarly, + [Values] bool suppressUnobservedRejections) + { + var cancelationSource = CancelationSource.New(); + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + Assert.True(runComplete); + cancelationSource.TryDispose(); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupIterationCancelationSourceDisposedEarly_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupIterationCanceledAfterAllCompleteAndBeforeMoveNextAsync_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupCancelGroupEarly_void( + [Values] bool disposeCancelationSourceEarly) + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(Promise.State.Canceled, asyncEnumerator.Current.State); + } + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupGroupCancelationSourceDisposedEarly_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupGroupCanceledAfterAllCompleteAndBeforeMoveNextAsync_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup2CancelationSources_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var groupCancelationSource = CancelationSource.New(); + var iterationCancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(groupCancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(iterationCancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length - 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + groupCancelationSource.Cancel(); + { + var (completeType, _, completeIndex) = args[args.Length - 2]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(Promise.State.Canceled, asyncEnumerator.Current.State); + } + iterationCancelationSource.Cancel(); + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + groupCancelationSource.Dispose(); + iterationCancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup1CancelationSourceUsedTwice_void() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseEachGroupResultsAreYieldedInCompletionOrder_T((CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] args) + { + Promise.Run(async () => + { + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(completeIndex, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupResultsAreYieldedInCompletionOrder_MoveNextAsyncBeforeComplete_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, false, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + foreach (var (completeType, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + bool movedNext = false; + var moveNextPromise = asyncEnumerator.MoveNextAsync() + .Finally(() => movedNext = true); + Assert.False(movedNext); + tryCompleters[completeIndex].Invoke(); + Assert.True(await moveNextPromise); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(completeIndex, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[completeIndex], resultContainer.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupDisposeEarly_T( + [Values] bool suppressUnobservedRejections) + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + Assert.True(runComplete); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupDisposeWithoutMoveNext_T( + [Values] bool suppressUnobservedRejections) + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + bool didThrow = false; + try + { + await eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).GetAsyncEnumerator().DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + foreach (var (_, _, completeIndex) in args.OrderBy(x => x.completeIndex)) + { + tryCompleters[completeIndex].Invoke(); + } + Assert.True(runComplete); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupCancelIterationEarly_T( + [Values] bool disposeCancelationSourceEarly, + [Values] bool suppressUnobservedRejections) + { + var cancelationSource = CancelationSource.New(); + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + bool runComplete = false; + var runPromise = Promise.Run(async () => + { + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var asyncEnumerator = eachGroup.GetAsyncEnumerable(suppressUnobservedRejections).WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.AreEqual(rejections[2], e.InnerExceptions[0]); + } + Assert.AreNotEqual(suppressUnobservedRejections, didThrow); + }, SynchronizationOption.Synchronous) + .Finally(() => runComplete = true); + + Assert.False(runComplete); + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + } + Assert.True(runComplete); + cancelationSource.TryDispose(); + + runPromise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupIterationCancelationSourceDisposedEarly_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupIterationCanceledAfterAllCompleteAndBeforeMoveNextAsync_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var eachGroup = PromiseEachGroup.New(out _); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + + var cancelationSource = CancelationSource.New(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupCancelGroupEarly_T( + [Values] bool disposeCancelationSourceEarly) + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + if (disposeCancelationSourceEarly) + { + cancelationSource.Dispose(); + } + + for (int i = args.Length / 2; i < args.Length; ++i) + { + tryCompleters[args[i].completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(Promise.State.Canceled, asyncEnumerator.Current.State); + } + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupGroupCancelationSourceDisposedEarly_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Dispose(); + + for (int i = args.Length / 2; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + await asyncEnumerator.DisposeAsync(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroupGroupCanceledAfterAllCompleteAndBeforeMoveNextAsync_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Cancel, false, 3), + (CompleteType.Reject, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.TryDispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup2CancelationSources_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var groupCancelationSource = CancelationSource.New(); + var iterationCancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(groupCancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(iterationCancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length - 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + groupCancelationSource.Cancel(); + { + var (completeType, _, completeIndex) = args[args.Length - 2]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + Assert.AreEqual(Promise.State.Canceled, asyncEnumerator.Current.State); + } + iterationCancelationSource.Cancel(); + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + groupCancelationSource.Dispose(); + iterationCancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup1CancelationSourceUsedTwice_T() + { + Promise.Run(async () => + { + var args = new (CompleteType completeType, bool isAlreadyComplete, int completeIndex)[] + { + (CompleteType.Resolve, false, 1), + (CompleteType.Resolve, true, 0), + (CompleteType.Resolve, false, 3), + (CompleteType.Resolve, false, 2), + }; + var rejections = new Exception[args.Length]; + var tryCompleters = new Action[args.Length]; + + var cancelationSource = CancelationSource.New(); + var eachGroup = PromiseEachGroup.New(cancelationSource.Token, out var groupCancelationToken); + for (int i = 0; i < args.Length; ++i) + { + var (completeType, isAlreadyComplete, completeIndex) = args[i]; + rejections[completeIndex] = new Exception($"Rejected completeIndex: {completeIndex}"); + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, completeIndex, rejections[completeIndex], groupCancelationToken, out tryCompleters[completeIndex]); + eachGroup = eachGroup.Add(promise); + } + + args = args.OrderBy(x => x.completeIndex).ToArray(); + var asyncEnumerator = eachGroup.GetAsyncEnumerable().WithCancelation(cancelationSource.Token).GetAsyncEnumerator(); + for (int i = 0; i < args.Length / 2; ++i) + { + var (completeType, _, completeIndex) = args[i]; + tryCompleters[completeIndex].Invoke(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(i, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(rejections[i], resultContainer.Reason); + } + } + cancelationSource.Cancel(); + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + cancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup_CancelationCallbackExceptionsArePropagated_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType iterationCancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool suppressUnobservedRejection) + { + Promise.Run(async () => + { + var groupCancelationSource = CancelationSource.New(); + var iterationCancelationSource = CancelationSource.New(); + + // We don't test a group cancelation source because it propagates the exceptions directly when it's canceled. + // We need to only test when the each group itself triggers cancelation (via the iteration cancelation source, or via DisposeAsync early). + var eachGroup = PromiseEachGroup.New(out var groupCancelationToken); + + var cancelationException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw cancelationException; }); + + var promiseException = new System.InvalidOperationException("Bang!"); + + var asyncEnumerator = eachGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, promiseException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, false, promiseException, out var tryCompleter2)) + .GetAsyncEnumerable(suppressUnobservedRejection) + .WithCancelation(iterationCancelationType == CancelationType.None ? CancelationToken.None : iterationCancelationSource.Token) + .GetAsyncEnumerator(); + + tryCompleter1(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType1, resultContainer.State); + if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(promiseException, resultContainer.Reason); + } + + groupCancelationSource.Cancel(); + iterationCancelationSource.Cancel(); + + tryCompleter2(); + if (iterationCancelationType == CancelationType.Deferred) + { + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + } + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + + int expectedExceptionCount = 1; + if (completeType2 == CompleteType.Reject && !suppressUnobservedRejection) + { + ++expectedExceptionCount; + } + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + if (expectedExceptionCount > 1) + { + Assert.IsInstanceOf(e.InnerExceptions[1]); + } + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(cancelationException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + } + + Assert.True(didThrow); + + groupCancelationSource.Dispose(); + iterationCancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + + [Test] + public void PromiseEachGroup_CancelationCallbackExceptionsArePropagated_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType iterationCancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool suppressUnobservedRejection) + { + Promise.Run(async () => + { + var groupCancelationSource = CancelationSource.New(); + var iterationCancelationSource = CancelationSource.New(); + + // We don't test a group cancelation source because it propagates the exceptions directly when it's canceled. + // We need to only test when the each group itself triggers cancelation (via the iteration cancelation source, or via DisposeAsync early). + var eachGroup = PromiseEachGroup.New(out var groupCancelationToken); + + var cancelationException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw cancelationException; }); + + var promiseException = new System.InvalidOperationException("Bang!"); + + var asyncEnumerator = eachGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, promiseException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, false, 2, promiseException, out var tryCompleter2)) + .GetAsyncEnumerable(suppressUnobservedRejection) + .WithCancelation(iterationCancelationType == CancelationType.None ? CancelationToken.None : iterationCancelationSource.Token) + .GetAsyncEnumerator(); + + tryCompleter1(); + Assert.True(await asyncEnumerator.MoveNextAsync()); + var resultContainer = asyncEnumerator.Current; + Assert.AreEqual((Promise.State) completeType1, resultContainer.State); + if (resultContainer.State == Promise.State.Resolved) + { + Assert.AreEqual(1, resultContainer.Value); + } + else if (resultContainer.State == Promise.State.Rejected) + { + Assert.AreEqual(promiseException, resultContainer.Reason); + } + + groupCancelationSource.Cancel(); + iterationCancelationSource.Cancel(); + + tryCompleter2(); + if (iterationCancelationType == CancelationType.Deferred) + { + await TestHelper.AssertCanceledAsync(() => asyncEnumerator.MoveNextAsync()); + } + + bool didThrow = false; + try + { + await asyncEnumerator.DisposeAsync(); + } + catch (AggregateException e) + { + didThrow = true; + + int expectedExceptionCount = 1; + if (completeType2 == CompleteType.Reject && !suppressUnobservedRejection) + { + ++expectedExceptionCount; + } + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + if (expectedExceptionCount > 1) + { + Assert.IsInstanceOf(e.InnerExceptions[1]); + } + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(cancelationException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + } + + Assert.True(didThrow); + + groupCancelationSource.Dispose(); + iterationCancelationSource.Dispose(); + }, SynchronizationOption.Synchronous) + .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs.meta new file mode 100644 index 00000000..46b7aa2d --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseEachGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: eb3f91d86ffb0d742bda8aec9a741f22 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs new file mode 100644 index 00000000..76237a79 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs @@ -0,0 +1,1282 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseMergeGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseMergeGroup_UsingInvalidatedGroupThrows( + [Values] CancelationType cancelationType) + { + var voidPromise = Promise.Resolved(); + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseMergeGroup).Add(voidPromise)); + Assert.Catch(() => default(PromiseMergeGroup).Add(intPromise)); + Assert.Catch(() => default(PromiseMergeGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup1 = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + var mergeGroup2 = mergeGroup1.Add(Promise.Resolved()); + Assert.Catch(() => mergeGroup1.Add(voidPromise)); + Assert.Catch(() => mergeGroup1.Add(intPromise)); + Assert.Catch(() => mergeGroup1.WaitAsync()); + + var mergeGroup3 = mergeGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup2.Add(voidPromise)); + Assert.Catch(() => mergeGroup2.Add(intPromise)); + Assert.Catch(() => mergeGroup2.WaitAsync()); + + var mergeGroup4 = mergeGroup3.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup3.Add(voidPromise)); + Assert.Catch(() => mergeGroup3.Add(intPromise)); + Assert.Catch(() => mergeGroup3.WaitAsync()); + + var mergeGroup5 = mergeGroup4.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup4.Add(voidPromise)); + Assert.Catch(() => mergeGroup4.Add(intPromise)); + Assert.Catch(() => mergeGroup4.WaitAsync()); + + var mergeGroup6 = mergeGroup5.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup5.Add(voidPromise)); + Assert.Catch(() => mergeGroup5.Add(intPromise)); + Assert.Catch(() => mergeGroup5.WaitAsync()); + + var mergeGroup7 = mergeGroup6.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup6.Add(voidPromise)); + Assert.Catch(() => mergeGroup6.Add(intPromise)); + Assert.Catch(() => mergeGroup6.WaitAsync()); + + var mergeGroup8 = mergeGroup7.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup7.Add(voidPromise)); + Assert.Catch(() => mergeGroup7.Add(intPromise)); + Assert.Catch(() => mergeGroup7.WaitAsync()); + + var mergeGroup9 = mergeGroup8.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup8.Add(voidPromise)); + Assert.Catch(() => mergeGroup8.Add(intPromise)); + Assert.Catch(() => mergeGroup8.WaitAsync()); + + var mergeGroup10 = mergeGroup9.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup9.Add(voidPromise)); + Assert.Catch(() => mergeGroup9.Add(intPromise)); + Assert.Catch(() => mergeGroup9.WaitAsync()); + + var mergeGroup11 = mergeGroup10.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup10.Add(voidPromise)); + Assert.Catch(() => mergeGroup10.Add(intPromise)); + Assert.Catch(() => mergeGroup10.WaitAsync()); + + mergeGroup11.WaitAsync().Forget(); + Assert.Catch(() => mergeGroup11.Add(voidPromise)); + Assert.Catch(() => mergeGroup11.Add(intPromise)); + Assert.Catch(() => mergeGroup11.WaitAsync()); + + voidPromise.Forget(); + intPromise.Forget(); + } + } + + [Test] + public void PromiseMergeGroupIsResolvedWhenNoPromisesAreAdded( + [Values] CancelationType cancelationType) + { + using (var cancelationSource = CancelationSource.New()) + { + bool resolved = false; + + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + mergeGroup + .WaitAsync() + .Then(() => resolved = true) + .Forget(); + + Assert.True(resolved); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_1_0( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else + { + Assert.AreEqual(completeType, (CompleteType) result.State); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_2_0( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_0_2( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual((value1, value2), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_2_2( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3and4, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value1, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3and4, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3and4, value2, expectedException, out var tryCompleter4)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject || completeType3 == CompleteType.Reject || completeType4 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel || completeType3 == CompleteType.Cancel || completeType4 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual((value1, value2), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete3and4, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete3and4, completed); + + tryCompleter4(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_1_3( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3and4, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values] CompleteType completeType4) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3and4, value2, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3and4, value3, expectedException, out var tryCompleter4)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject || completeType3 == CompleteType.Reject || completeType4 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel || completeType3 == CompleteType.Cancel || completeType4 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual((value1, value2, value3), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete3and4, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete3and4, completed); + + tryCompleter4(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_0_7( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3Through7, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + ulong value5 = ulong.MaxValue; + TimeSpan value6 = TimeSpan.FromSeconds(2); + Promise.State value7 = Promise.State.Canceled; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3Through7, value3, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3Through7, value4, expectedException, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete3Through7, value5, expectedException, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete3Through7, value6, expectedException, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete3Through7, value7, expectedException, out var tryCompleter7)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject || completeType3 == CompleteType.Reject || completeType4 == CompleteType.Reject + || completeType5 == CompleteType.Reject || completeType6 == CompleteType.Reject || completeType7 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel || completeType3 == CompleteType.Cancel || completeType4 == CompleteType.Cancel + || completeType5 == CompleteType.Cancel || completeType6 == CompleteType.Cancel || completeType7 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual((value1, value2, value3, value4, value5, value6, value7), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2 && alreadyComplete3Through7, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2 && alreadyComplete3Through7, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter7(); + + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_0_8( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out _) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + ulong value5 = ulong.MaxValue; + TimeSpan value6 = TimeSpan.FromSeconds(2); + Promise.State value7 = Promise.State.Canceled; + int value8 = 2; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, value2, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, value3, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, value4, expectedException, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, value5, expectedException, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, value6, expectedException, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, value7, expectedException, out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, value8, expectedException, out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject || completeType3 == CompleteType.Reject || completeType4 == CompleteType.Reject + || completeType5 == CompleteType.Reject || completeType6 == CompleteType.Reject || completeType7 == CompleteType.Reject || completeType8 == CompleteType.Reject) + { + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType1 == CompleteType.Cancel || completeType2 == CompleteType.Cancel || completeType3 == CompleteType.Cancel || completeType4 == CompleteType.Cancel + || completeType5 == CompleteType.Cancel || completeType6 == CompleteType.Cancel || completeType7 == CompleteType.Cancel || completeType8 == CompleteType.Cancel) + { + Assert.AreEqual(Promise.State.Canceled, result.State); + } + else + { + Assert.AreEqual(Promise.State.Resolved, result.State); + Assert.AreEqual(((value1, value2, value3, value4, value5, value6, value7), value8), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter7(); + Assert.AreEqual(alreadyComplete8, completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + + private static Promise.State GetExpectedState(CancelationType cancelationType, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + if (cancelationType == CancelationType.Immediate) + { + return Promise.State.Canceled; + } + + bool allAlreadyComplete = true; + foreach (var cv in completeValues) + { + if (cv.alreadyComplete) + { + if (cv.completeType != CompleteType.Resolve) + { + return (Promise.State) cv.completeType; + } + } + else + { + allAlreadyComplete = false; + } + } + if (allAlreadyComplete) + { + return Promise.State.Resolved; + } + + if (cancelationType == CancelationType.None) + { + foreach (var cv in completeValues) + { + if (cv.completeType != CompleteType.Resolve) + { + return (Promise.State) cv.completeType; + } + } + return Promise.State.Resolved; + } + + // CancelationType.Deferred - cancelation source is canceled after the first promise is completed. + if (completeValues[0].completeType != CompleteType.Resolve) + { + return (Promise.State) completeValues[0].completeType; + } + if (completeValues.Skip(1).All(cv => cv.alreadyComplete && cv.completeType == CompleteType.Resolve)) + { + return Promise.State.Resolved; + } + return Promise.State.Canceled; + } + + private static bool GetShouldBeComplete(int indexOfCompletion, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + // indexOfCompletion = 0 means none explicitly completed yet. + for (int i = 0; i < indexOfCompletion; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + bool allAlreadyComplete = true; + for (int i = indexOfCompletion; i < completeValues.Length; ++i) + { + if (!completeValues[i].alreadyComplete) + { + allAlreadyComplete = false; + continue; + } + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + return allAlreadyComplete; + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_2_0( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_0_2( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual((value1, value2), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_2_2( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3and4, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2), + (completeType3, alreadyComplete3and4), + (completeType4, alreadyComplete3and4) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value1, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3and4, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3and4, value2, expectedException, groupCancelationToken, out var tryCompleter4)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual((value1, value2), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_0_7( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3Through7, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + ulong value5 = ulong.MaxValue; + TimeSpan value6 = TimeSpan.FromSeconds(2); + Promise.State value7 = Promise.State.Canceled; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2), + (completeType3, alreadyComplete3Through7), + (completeType4, alreadyComplete3Through7), + (completeType5, alreadyComplete3Through7), + (completeType6, alreadyComplete3Through7), + (completeType7, alreadyComplete3Through7) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3Through7, value3, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3Through7, value4, expectedException, groupCancelationToken, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete3Through7, value5, expectedException, groupCancelationToken, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete3Through7, value6, expectedException, groupCancelationToken, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete3Through7, value7, expectedException, groupCancelationToken, out var tryCompleter7)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual((value1, value2, value3, value4, value5, value6, value7), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(4, completeValues), + completed); + + tryCompleter5(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(5, completeValues), + completed); + + tryCompleter6(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(6, completeValues), + completed); + + tryCompleter7(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroupIsCompletedWhenAllPromisesAreCompleted_WithCancelation_0_8( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + ulong value5 = ulong.MaxValue; + TimeSpan value6 = TimeSpan.FromSeconds(2); + Promise.State value7 = Promise.State.Canceled; + int value8 = 2; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2Through7), + (completeType3, alreadyComplete2Through7), + (completeType4, alreadyComplete2Through7), + (completeType5, alreadyComplete2Through7), + (completeType6, alreadyComplete2Through7), + (completeType7, alreadyComplete2Through7), + (completeType8, alreadyComplete8) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, value3, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, value4, expectedException, groupCancelationToken, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, value5, expectedException, groupCancelationToken, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, value6, expectedException, groupCancelationToken, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, value7, expectedException, groupCancelationToken, out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, value8, expectedException, groupCancelationToken, out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(cancelationType, completeValues); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(((value1, value2, value3, value4, value5, value6, value7), value8), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(4, completeValues), + completed); + + tryCompleter5(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(5, completeValues), + completed); + + tryCompleter6(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(6, completeValues), + completed); + + tryCompleter7(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(7, completeValues), + completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroup_CancelationCallbackExceptionsArePropagated_2( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + if (expectedExceptionCount > 1) + { + Assert.IsInstanceOf(e.InnerExceptions[1]); + } + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeGroup_CancelationCallbackExceptionsArePropagated_8( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeGroup.New(out var groupCancelationToken) + : PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, new System.InvalidOperationException("Bang!"), out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType8 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + if (expectedExceptionCount > 1) + { + Assert.IsInstanceOf(e.InnerExceptions[1]); + } + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter7(); + Assert.AreEqual(alreadyComplete8, completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs.meta new file mode 100644 index 00000000..e7ba2896 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9a8ae7673c26e5849a0f2756133d8d9d +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs new file mode 100644 index 00000000..05fafc62 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs @@ -0,0 +1,1370 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseMergeResultsGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseMergeResultsGroup_UsingInvalidatedGroupThrows( + [Values] CancelationType cancelationType) + { + var voidPromise = Promise.Resolved(); + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseMergeResultsGroup).Add(voidPromise)); + Assert.Catch(() => default(PromiseMergeResultsGroup).Add(intPromise)); + + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup1 = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + var mergeGroup2 = mergeGroup1.Add(Promise.Resolved()); + Assert.Catch(() => mergeGroup1.Add(voidPromise)); + Assert.Catch(() => mergeGroup1.Add(intPromise)); + + var mergeGroup3 = mergeGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup2.Add(voidPromise)); + Assert.Catch(() => mergeGroup2.Add(intPromise)); + + var mergeGroup4 = mergeGroup3.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup3.Add(voidPromise)); + Assert.Catch(() => mergeGroup3.Add(intPromise)); + Assert.Catch(() => mergeGroup3.WaitAsync()); + + var mergeGroup5 = mergeGroup4.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup4.Add(voidPromise)); + Assert.Catch(() => mergeGroup4.Add(intPromise)); + Assert.Catch(() => mergeGroup4.WaitAsync()); + + var mergeGroup6 = mergeGroup5.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup5.Add(voidPromise)); + Assert.Catch(() => mergeGroup5.Add(intPromise)); + Assert.Catch(() => mergeGroup5.WaitAsync()); + + var mergeGroup7 = mergeGroup6.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup6.Add(voidPromise)); + Assert.Catch(() => mergeGroup6.Add(intPromise)); + Assert.Catch(() => mergeGroup6.WaitAsync()); + + var mergeGroup8 = mergeGroup7.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup7.Add(voidPromise)); + Assert.Catch(() => mergeGroup7.Add(intPromise)); + Assert.Catch(() => mergeGroup7.WaitAsync()); + +#if ENABLE_IL2CPP + mergeGroup8.WaitAsync().Forget(); + Assert.Catch(() => mergeGroup8.Add(voidPromise)); + Assert.Catch(() => mergeGroup8.Add(intPromise)); + Assert.Catch(() => mergeGroup8.WaitAsync()); +#else + var mergeGroup9 = mergeGroup8.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup8.Add(voidPromise)); + Assert.Catch(() => mergeGroup8.Add(intPromise)); + Assert.Catch(() => mergeGroup8.WaitAsync()); + + var mergeGroup10 = mergeGroup9.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup9.Add(voidPromise)); + Assert.Catch(() => mergeGroup9.Add(intPromise)); + Assert.Catch(() => mergeGroup9.WaitAsync()); + + var mergeGroup11 = mergeGroup10.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup10.Add(voidPromise)); + Assert.Catch(() => mergeGroup10.Add(intPromise)); + Assert.Catch(() => mergeGroup10.WaitAsync()); + + var mergeGroup12 = mergeGroup11.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup11.Add(voidPromise)); + Assert.Catch(() => mergeGroup11.Add(intPromise)); + Assert.Catch(() => mergeGroup11.WaitAsync()); + + var mergeGroup13 = mergeGroup12.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup12.Add(voidPromise)); + Assert.Catch(() => mergeGroup12.Add(intPromise)); + Assert.Catch(() => mergeGroup12.WaitAsync()); + + var mergeGroup14 = mergeGroup13.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup13.Add(voidPromise)); + Assert.Catch(() => mergeGroup13.Add(intPromise)); + Assert.Catch(() => mergeGroup13.WaitAsync()); + + var mergeGroup15 = mergeGroup14.Add(Promise.Resolved(2)); + Assert.Catch(() => mergeGroup14.Add(voidPromise)); + Assert.Catch(() => mergeGroup14.Add(intPromise)); + Assert.Catch(() => mergeGroup14.WaitAsync()); + + mergeGroup15.WaitAsync().Forget(); + Assert.Catch(() => mergeGroup15.Add(voidPromise)); + Assert.Catch(() => mergeGroup15.Add(intPromise)); + Assert.Catch(() => mergeGroup15.WaitAsync()); +#endif + + voidPromise.Forget(); + intPromise.Forget(); + } + } + + private static void AssertResult(CompleteType completeType, Promise.ResultContainer resultContainer, TReject expectedReason) + { + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedReason, resultContainer.Reason); + } + } + + private static void AssertResult(CompleteType completeType, Promise.ResultContainer resultContainer, TResult expectedResult, TReject expectedReason) + { + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(expectedResult, resultContainer.Value); + } + else if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedReason, resultContainer.Reason); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_void_void( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2) = result.Value; + AssertResult(completeType1, result1, expectedException); + AssertResult(completeType2, result2, expectedException); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_T_T( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2) = result.Value; + AssertResult(completeType1, result1, expectedException); + AssertResult(completeType2, result2, expectedException); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_void_T_void_T( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3and4, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value1, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3and4, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3and4, value2, expectedException, out var tryCompleter4)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2, result3, result4) = result.Value; + AssertResult(completeType1, result1, expectedException); + AssertResult(completeType2, result2, value1, expectedException); + AssertResult(completeType3, result3, expectedException); + AssertResult(completeType4, result4, value2, expectedException); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2 && alreadyComplete3and4, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete3and4, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete3and4, completed); + + tryCompleter4(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_T_T_T_T_void_void_void( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3Through7, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3Through7, value3, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3Through7, value4, expectedException, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete3Through7, expectedException, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete3Through7, expectedException, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete3Through7, expectedException, out var tryCompleter7)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2, result3, result4, result5, result6, result7) = result.Value; + AssertResult(completeType1, result1, value1, expectedException); + AssertResult(completeType2, result2, value2, expectedException); + AssertResult(completeType3, result3, value3, expectedException); + AssertResult(completeType4, result4, value4, expectedException); + AssertResult(completeType5, result5, expectedException); + AssertResult(completeType6, result6, expectedException); + AssertResult(completeType7, result7, expectedException); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2 && alreadyComplete3Through7, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2 && alreadyComplete3Through7, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete3Through7, completed); + tryCompleter7(); + + Assert.IsTrue(completed); + } + } + + private static bool GetShouldBeComplete(int indexOfCompletion, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + // indexOfCompletion = 0 means none explicitly completed yet. + for (int i = 0; i < indexOfCompletion; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + bool allAlreadyComplete = true; + for (int i = indexOfCompletion; i < completeValues.Length; ++i) + { + if (!completeValues[i].alreadyComplete) + { + allAlreadyComplete = false; + continue; + } + if (completeValues[i].completeType != CompleteType.Resolve) + { + return true; + } + } + return allAlreadyComplete; + } + + private static CompleteType GetExpectedState(int index, CancelationType cancelationType, (CompleteType completeType, bool alreadyComplete)[] completeValues) + { + if (cancelationType == CancelationType.Immediate) + { + return CompleteType.Cancel; + } + + for (int i = 0; i < index; ++i) + { + var cv = completeValues[i]; + if (cv.alreadyComplete && cv.completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + if (completeValues[index].alreadyComplete) + { + return completeValues[index].completeType; + } + + for (int i = index + 1; i < completeValues.Length; ++i) + { + var cv = completeValues[i]; + if (cv.alreadyComplete && cv.completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + if (cancelationType == CancelationType.Deferred) + { + // Cancelation source is canceled after the first promise is completed. + if (index == 0) + { + return completeValues[0].completeType; + } + + return CompleteType.Cancel; + } + + // CancelationType.None + + for (int i = 0; i < index; ++i) + { + if (completeValues[i].completeType != CompleteType.Resolve) + { + return CompleteType.Cancel; + } + } + + return completeValues[index].completeType; + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_void_void( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_T_T( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, value1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, value2, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_void_T_void_T( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3and4, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2), + (completeType3, alreadyComplete3and4), + (completeType4, alreadyComplete3and4) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value1, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3and4, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3and4, value2, expectedException, groupCancelationToken, out var tryCompleter4)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2, result3, result4) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, value1, expectedException); + AssertResult(GetExpectedState(2, cancelationType, completeValues), result3, expectedException); + AssertResult(GetExpectedState(3, cancelationType, completeValues), result4, value2, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_T_T_T_T_void_void_void( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + [Values] bool alreadyComplete2, + // Reduce number of tests. + [Values(false)] bool alreadyComplete3Through7, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value2 = "Success"; + bool value3 = true; + float value4 = 1.0f; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2), + (completeType3, alreadyComplete3Through7), + (completeType4, alreadyComplete3Through7), + (completeType5, alreadyComplete3Through7), + (completeType6, alreadyComplete3Through7), + (completeType7, alreadyComplete3Through7) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete3Through7, value3, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete3Through7, value4, expectedException, groupCancelationToken, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete3Through7, expectedException, groupCancelationToken, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete3Through7, expectedException, groupCancelationToken, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete3Through7, expectedException, groupCancelationToken, out var tryCompleter7)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (result1, result2, result3, result4, result5, result6, result7) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, value1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, value2, expectedException); + AssertResult(GetExpectedState(2, cancelationType, completeValues), result3, value3, expectedException); + AssertResult(GetExpectedState(3, cancelationType, completeValues), result4, value4, expectedException); + AssertResult(GetExpectedState(4, cancelationType, completeValues), result5, expectedException); + AssertResult(GetExpectedState(5, cancelationType, completeValues), result6, expectedException); + AssertResult(GetExpectedState(6, cancelationType, completeValues), result7, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(4, completeValues), + completed); + + tryCompleter5(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(5, completeValues), + completed); + + tryCompleter6(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(6, completeValues), + completed); + + tryCompleter7(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroup_CancelationCallbackExceptionsArePropagated_2( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_T_void_T_void_T_T_void_void( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out _) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out _) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out _); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value3 = "Success"; + bool value5 = true; + float value6 = 1.0f; + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, expectedException, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, value3, expectedException, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, expectedException, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, value5, expectedException, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, value6, expectedException, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, expectedException, out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, expectedException, out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var ((result1, result2, result3, result4, result5, result6, result7), result8) = result.Value; + AssertResult(completeType1, result1, value1, expectedException); + AssertResult(completeType2, result2, expectedException); + AssertResult(completeType3, result3, value3, expectedException); + AssertResult(completeType4, result4, expectedException); + AssertResult(completeType5, result5, value5, expectedException); + AssertResult(completeType6, result6, value6, expectedException); + AssertResult(completeType7, result7, expectedException); + AssertResult(completeType8, result8, expectedException); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter7(); + Assert.AreEqual(alreadyComplete8, completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_T_void_T_void_T_T_void_T( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value3 = "Success"; + bool value5 = true; + float value6 = 1.0f; + TimeSpan value8 = TimeSpan.FromSeconds(2); + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2Through7), + (completeType3, alreadyComplete2Through7), + (completeType4, alreadyComplete2Through7), + (completeType5, alreadyComplete2Through7), + (completeType6, alreadyComplete2Through7), + (completeType7, alreadyComplete2Through7), + (completeType8, alreadyComplete8) + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, value3, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, expectedException, groupCancelationToken, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, value5, expectedException, groupCancelationToken, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, value6, expectedException, groupCancelationToken, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, expectedException, groupCancelationToken, out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, value8, expectedException, groupCancelationToken, out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var ((result1, result2, result3, result4, result5, result6, result7), result8) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, value1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, expectedException); + AssertResult(GetExpectedState(2, cancelationType, completeValues), result3, value3, expectedException); + AssertResult(GetExpectedState(3, cancelationType, completeValues), result4, expectedException); + AssertResult(GetExpectedState(4, cancelationType, completeValues), result5, value5, expectedException); + AssertResult(GetExpectedState(5, cancelationType, completeValues), result6, value6, expectedException); + AssertResult(GetExpectedState(6, cancelationType, completeValues), result7, expectedException); + AssertResult(GetExpectedState(7, cancelationType, completeValues), result8, value8, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(4, completeValues), + completed); + + tryCompleter5(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(5, completeValues), + completed); + + tryCompleter6(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(6, completeValues), + completed); + + tryCompleter7(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(7, completeValues), + completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroupIsResolvedWhenAllPromisesAreCompleted_WithCancelation_14( + [Values] CancelationType cancelationType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through13, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values(CompleteType.Resolve)] CompleteType completeType8, + [Values(CompleteType.Resolve)] CompleteType completeType9, + [Values(CompleteType.Resolve)] CompleteType completeType10, + [Values(CompleteType.Resolve)] CompleteType completeType11, + [Values(CompleteType.Resolve)] CompleteType completeType12, + [Values(CompleteType.Resolve)] CompleteType completeType13, + [Values] CompleteType completeType14, + [Values] bool alreadyComplete14) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : cancelationType == CancelationType.Deferred ? PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken) + : PromiseMergeResultsGroup.New(CancelationToken.Canceled(), out groupCancelationToken); + + Exception expectedException = new Exception("Bang!"); + int value1 = 42; + string value3 = "Success"; + bool value5 = true; + float value6 = 1.0f; + TimeSpan value8 = TimeSpan.FromSeconds(2); + long value14 = long.MaxValue; + + bool completed = false; + + var completeValues = new[] + { + (completeType1, alreadyComplete1), + (completeType2, alreadyComplete2Through13), + (completeType3, alreadyComplete2Through13), + (completeType4, alreadyComplete2Through13), + (completeType5, alreadyComplete2Through13), + (completeType6, alreadyComplete2Through13), + (completeType7, alreadyComplete2Through13), + (completeType8, alreadyComplete2Through13), + (completeType9, alreadyComplete2Through13), + (completeType10, alreadyComplete2Through13), + (completeType11, alreadyComplete2Through13), + (completeType12, alreadyComplete2Through13), + (completeType13, alreadyComplete2Through13), + (completeType14, alreadyComplete14), + }; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through13, value3, expectedException, groupCancelationToken, out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through13, value5, expectedException, groupCancelationToken, out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through13, value6, expectedException, groupCancelationToken, out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete2Through13, value8, expectedException, groupCancelationToken, out var tryCompleter8)) + .Add(TestHelper.BuildPromise(completeType9, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter9)) + .Add(TestHelper.BuildPromise(completeType10, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter10)) + .Add(TestHelper.BuildPromise(completeType11, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter11)) + .Add(TestHelper.BuildPromise(completeType12, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter12)) + .Add(TestHelper.BuildPromise(completeType13, alreadyComplete2Through13, expectedException, groupCancelationToken, out var tryCompleter13)) + .Add(TestHelper.BuildPromise(completeType14, alreadyComplete14, value14, expectedException, groupCancelationToken, out var tryCompleter14)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Resolved, result.State); + var (((result1, result2, result3, result4, result5, result6, result7), result8, result9, result10, result11, result12, result13), result14) = result.Value; + AssertResult(GetExpectedState(0, cancelationType, completeValues), result1, value1, expectedException); + AssertResult(GetExpectedState(1, cancelationType, completeValues), result2, expectedException); + AssertResult(GetExpectedState(2, cancelationType, completeValues), result3, value3, expectedException); + AssertResult(GetExpectedState(3, cancelationType, completeValues), result4, expectedException); + AssertResult(GetExpectedState(4, cancelationType, completeValues), result5, value5, expectedException); + AssertResult(GetExpectedState(5, cancelationType, completeValues), result6, value6, expectedException); + AssertResult(GetExpectedState(6, cancelationType, completeValues), result7, expectedException); + AssertResult(GetExpectedState(7, cancelationType, completeValues), result8, value8, expectedException); + AssertResult(GetExpectedState(8, cancelationType, completeValues), result9, expectedException); + AssertResult(GetExpectedState(9, cancelationType, completeValues), result10, expectedException); + AssertResult(GetExpectedState(10, cancelationType, completeValues), result11, expectedException); + AssertResult(GetExpectedState(11, cancelationType, completeValues), result12, expectedException); + AssertResult(GetExpectedState(12, cancelationType, completeValues), result13, expectedException); + AssertResult(GetExpectedState(13, cancelationType, completeValues), result14, value14, expectedException); + }) + .Forget(); + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(0, completeValues), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || GetShouldBeComplete(1, completeValues), + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(1, completeValues), + completed); + + tryCompleter2(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(2, completeValues), + completed); + + tryCompleter3(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(3, completeValues), + completed); + + tryCompleter4(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(4, completeValues), + completed); + + tryCompleter5(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(5, completeValues), + completed); + + tryCompleter6(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(6, completeValues), + completed); + + tryCompleter7(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(7, completeValues), + completed); + + tryCompleter8(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(8, completeValues), + completed); + + tryCompleter9(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(9, completeValues), + completed); + + tryCompleter10(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(10, completeValues), + completed); + + tryCompleter11(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(11, completeValues), + completed); + + tryCompleter12(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(12, completeValues), + completed); + + tryCompleter13(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || GetShouldBeComplete(13, completeValues), + completed); + + tryCompleter14(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroup_CancelationCallbackExceptionsArePropagated_8( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through7, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType8, + [Values] bool alreadyComplete8) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through7, new System.InvalidOperationException("Bang!"), out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete8, new System.InvalidOperationException("Bang!"), out var tryCompleter8)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete2Through7 && alreadyComplete8, completed); + + tryCompleter7(); + Assert.AreEqual(alreadyComplete8, completed); + + tryCompleter8(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseMergeResultsGroup_CancelationCallbackExceptionsArePropagated_14( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + // Reduce number of tests. + [Values(false)] bool alreadyComplete2Through13, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + [Values(CompleteType.Resolve)] CompleteType completeType4, + [Values(CompleteType.Resolve)] CompleteType completeType5, + [Values(CompleteType.Resolve)] CompleteType completeType6, + [Values(CompleteType.Resolve)] CompleteType completeType7, + [Values(CompleteType.Resolve)] CompleteType completeType8, + [Values(CompleteType.Resolve)] CompleteType completeType9, + [Values(CompleteType.Resolve)] CompleteType completeType10, + [Values(CompleteType.Resolve)] CompleteType completeType11, + [Values(CompleteType.Resolve)] CompleteType completeType12, + [Values(CompleteType.Resolve)] CompleteType completeType13, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType14, + [Values] bool alreadyComplete14) + { + using (var cancelationSource = CancelationSource.New()) + { + var mergeGroup = cancelationType == CancelationType.None ? PromiseMergeResultsGroup.New(out var groupCancelationToken) + : PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + mergeGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .Add(TestHelper.BuildPromise(completeType3, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter3)) + .Add(TestHelper.BuildPromise(completeType4, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter4)) + .Add(TestHelper.BuildPromise(completeType5, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter5)) + .Add(TestHelper.BuildPromise(completeType6, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter6)) + .Add(TestHelper.BuildPromise(completeType7, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter7)) + .Add(TestHelper.BuildPromise(completeType8, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter8)) + .Add(TestHelper.BuildPromise(completeType9, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter9)) + .Add(TestHelper.BuildPromise(completeType10, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter10)) + .Add(TestHelper.BuildPromise(completeType11, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter11)) + .Add(TestHelper.BuildPromise(completeType12, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter12)) + .Add(TestHelper.BuildPromise(completeType13, alreadyComplete2Through13, new System.InvalidOperationException("Bang!"), out var tryCompleter13)) + .Add(TestHelper.BuildPromise(completeType14, alreadyComplete14, new System.InvalidOperationException("Bang!"), out var tryCompleter14)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(1, e.InnerExceptions.Count); + Assert.IsInstanceOf(e.InnerExceptions[0]); + Assert.AreEqual(1, e.InnerExceptions[0].UnsafeAs().InnerExceptions.Count); + Assert.AreEqual(expectedException, e.InnerExceptions[0].UnsafeAs().InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2Through13 && alreadyComplete14, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + + tryCompleter2(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter3(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter4(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter5(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter6(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter7(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter8(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter9(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter10(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter11(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + tryCompleter12(); + Assert.AreEqual(alreadyComplete2Through13 && alreadyComplete14, completed); + + tryCompleter13(); + Assert.AreEqual(alreadyComplete14, completed); + + tryCompleter14(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs.meta new file mode 100644 index 00000000..63df6ad1 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseMergeResultsGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 80f3ce30eec807f4586fed2e245584cc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs new file mode 100644 index 00000000..92c02dfb --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs @@ -0,0 +1,786 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseRaceGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseRaceGroup_UsingInvalidatedGroupThrows_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + var voidPromise = Promise.Resolved(); + Assert.Catch(() => default(PromiseRaceGroup).Add(voidPromise)); + Assert.Catch(() => default(PromiseRaceGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup1 = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + var raceGroup2 = raceGroup1.Add(Promise.Resolved()); + Assert.Catch(() => raceGroup1.Add(voidPromise)); + Assert.Catch(() => raceGroup1.WaitAsync()); + + var raceGroup3 = raceGroup2.Add(Promise.Resolved()); + Assert.Catch(() => raceGroup2.Add(voidPromise)); + Assert.Catch(() => raceGroup2.WaitAsync()); + + raceGroup3.WaitAsync().Forget(); + Assert.Catch(() => raceGroup3.Add(voidPromise)); + Assert.Catch(() => raceGroup3.WaitAsync()); + + voidPromise.Forget(); + } + } + + [Test] + public void PromiseRaceGroupThrowsWhenNoPromisesAreAdded_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Assert.Catch(() => raceGroup.WaitAsync()); + + raceGroup.Add(Promise.Resolved()).WaitAsync().Forget(); + } + } + + private static Promise.State GetExpectedState(CompleteType completeType1, CompleteType completeType2) + { + if (completeType1 == CompleteType.Resolve || completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject) + { + return Promise.State.Rejected; + } + return Promise.State.Canceled; + } + + [Test] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_1_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual((Promise.State) completeType, result.State); + if (completeType == CompleteType.Reject) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + private static IEnumerable GetArgs() + { + var cancelationTypes = new[] { CancelationType.None, CancelationType.Deferred, CancelationType.Immediate }; + var completeTypes = new[] { CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel }; + var bools = new[] { true, false }; + var falseOnly = new[] { false }; + + foreach (var cancelationType in cancelationTypes) + foreach (var cancelOnNonResolved in bools) + foreach (var completeType1 in completeTypes) + foreach (var alreadyComplete1 in bools) + foreach (var completeType2 in completeTypes) + foreach (var alreadyComplete2 in bools) + foreach (var completeFirstPromiseFirst in !alreadyComplete1 && !alreadyComplete2 ? bools : falseOnly) + { + yield return new TestCaseData(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + } + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(completeType1, completeType2); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + }) + .Forget(); + + if (!completeFirstPromiseFirst) + { + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + private static Promise.State GetExpectedState(CancelationType cancelationType, bool cancelOnNonResolved, + CompleteType completeType1, bool alreadyComplete1, CompleteType completeType2, bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + if (cancelationType == CancelationType.Immediate) + { + return Promise.State.Canceled; + } + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + if (alreadyComplete1) + { + if (completeType1 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + + if (cancelOnNonResolved) + { + return (Promise.State) completeType1; + } + + if (alreadyComplete2) + { + if (completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + if (completeType2 == CompleteType.Cancel) + { + return (Promise.State) completeType1; + } + return Promise.State.Rejected; + } + + if (cancelationType == CancelationType.Deferred) + { + return (Promise.State) completeType1; + } + if (completeType2 == CompleteType.Cancel) + { + return (Promise.State) completeType1; + } + return (Promise.State) completeType2; + } + + if (alreadyComplete2) + { + if (completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + + if (cancelOnNonResolved) + { + return (Promise.State) completeType2; + } + + if (completeType1 == CompleteType.Cancel) + { + return (Promise.State) completeType2; + } + return (Promise.State) completeType1; + } + + if (cancelationType == CancelationType.Deferred) + { + return (Promise.State) completeType1; + } + + // CancelationType.None + if (cancelOnNonResolved) + { + return (Promise.State) completeType1; + } + + return GetExpectedState(completeType1, completeType2); + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_WithCancelation_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + var expectedState = GetExpectedState(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + }) + .Forget(); + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || (alreadyComplete1 && alreadyComplete2) + || (cancelOnNonResolved && (alreadyComplete1 || alreadyComplete2)) + || (alreadyComplete1 && completeType1 == CompleteType.Resolve), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceGroup_NonResolved_CancelationCallbackExceptionsArePropagated_2_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_void(cancelationType, true, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + [Test] + public void PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values(CompleteType.Resolve)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_void(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + private static void Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + var cancelationAggregateException = e.InnerExceptions.OfType().Single(); + Assert.AreEqual(expectedExceptionCount - 1, e.InnerExceptions.OfType().Count()); + Assert.AreEqual(1, cancelationAggregateException.InnerExceptions.Count); + Assert.AreEqual(expectedException, cancelationAggregateException.InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceGroup_UsingInvalidatedGroupThrows_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseRaceGroup).Add(intPromise)); + Assert.Catch(() => default(PromiseRaceGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup1 = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + var raceGroup2 = raceGroup1.Add(Promise.Resolved(2)); + Assert.Catch(() => raceGroup1.Add(intPromise)); + Assert.Catch(() => raceGroup1.WaitAsync()); + + var raceGroup3 = raceGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => raceGroup2.Add(intPromise)); + Assert.Catch(() => raceGroup2.WaitAsync()); + + raceGroup3.WaitAsync().Forget(); + Assert.Catch(() => raceGroup3.Add(intPromise)); + Assert.Catch(() => raceGroup3.WaitAsync()); + + intPromise.Forget(); + } + } + + [Test] + public void PromiseRaceGroupThrowsWhenNoPromisesAreAdded_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Assert.Catch(() => raceGroup.WaitAsync()); + + raceGroup.Add(Promise.Resolved(2)).WaitAsync().Forget(); + } + } + + [Test] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_1_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, value1, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual((Promise.State) completeType, result.State); + if (completeType == CompleteType.Reject) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(value1, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + private static T GetExpectedValue(T value1, T value2, + CompleteType completeType1, bool alreadyComplete1, CompleteType completeType2, bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + (value1, value2) = (value2, value1); + } + + if (alreadyComplete1) + { + return completeType1 == CompleteType.Resolve + ? value1 + : value2; + } + + if (alreadyComplete2) + { + return completeType2 == CompleteType.Resolve + ? value2 + : value1; + } + + return completeType1 == CompleteType.Resolve + ? value1 + : value2; + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + int value1 = 1; + int value2 = 2; + Exception expectedException = new Exception("Bang!"); + int expectedValue = GetExpectedValue(value1, value2, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(completeType1, completeType2); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!completeFirstPromiseFirst) + { + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceGroupAdoptsTheStateOfTheFirstCompletedPromise_WithCancelation_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + int value1 = 1; + int value2 = 2; + Exception expectedException = new Exception("Bang!"); + int expectedValue = GetExpectedValue(value1, value2, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + var expectedState = GetExpectedState(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || (alreadyComplete1 && alreadyComplete2) + || (cancelOnNonResolved && (alreadyComplete1 || alreadyComplete2)) + || (alreadyComplete1 && completeType1 == CompleteType.Resolve), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceGroup_NonResolved_CancelationCallbackExceptionsArePropagated_2_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_T(cancelationType, true, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + [Test] + public void PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values(CompleteType.Resolve)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_T(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + private static void Test_PromiseRaceGroup_CancelationCallbackExceptionsArePropagated_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + var cancelationAggregateException = e.InnerExceptions.OfType().Single(); + Assert.AreEqual(expectedExceptionCount - 1, e.InnerExceptions.OfType().Count()); + Assert.AreEqual(1, cancelationAggregateException.InnerExceptions.Count); + Assert.AreEqual(expectedException, cancelationAggregateException.InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs.meta new file mode 100644 index 00000000..df5292ce --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8be4e489a0b39d6418967eade960f9d8 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs new file mode 100644 index 00000000..aaa77fc9 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs @@ -0,0 +1,800 @@ +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.APIs.PromiseGroups +{ + public class PromiseRaceWithIndexGroupTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void PromiseRaceWithIndexGroup_UsingInvalidatedGroupThrows_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + var voidPromise = Promise.Resolved(); + Assert.Catch(() => default(PromiseRaceWithIndexGroup).Add(voidPromise)); + Assert.Catch(() => default(PromiseRaceWithIndexGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup1 = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + var raceGroup2 = raceGroup1.Add(Promise.Resolved()); + Assert.Catch(() => raceGroup1.Add(voidPromise)); + Assert.Catch(() => raceGroup1.WaitAsync()); + + var raceGroup3 = raceGroup2.Add(Promise.Resolved()); + Assert.Catch(() => raceGroup2.Add(voidPromise)); + Assert.Catch(() => raceGroup2.WaitAsync()); + + raceGroup3.WaitAsync().Forget(); + Assert.Catch(() => raceGroup3.Add(voidPromise)); + Assert.Catch(() => raceGroup3.WaitAsync()); + + voidPromise.Forget(); + } + } + + [Test] + public void PromiseRaceWithIndexGroupThrowsWhenNoPromisesAreAdded_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Assert.Catch(() => raceGroup.WaitAsync()); + + raceGroup.Add(Promise.Resolved()).WaitAsync().Forget(); + } + } + + private static Promise.State GetExpectedState(CompleteType completeType1, CompleteType completeType2) + { + if (completeType1 == CompleteType.Resolve || completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + if (completeType1 == CompleteType.Reject || completeType2 == CompleteType.Reject) + { + return Promise.State.Rejected; + } + return Promise.State.Canceled; + } + + [Test] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_1_void( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual((Promise.State) completeType, result.State); + if (completeType == CompleteType.Reject) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType == CompleteType.Resolve) + { + Assert.AreEqual(0, result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + private static IEnumerable GetArgs() + { + var cancelationTypes = new[] { CancelationType.None, CancelationType.Deferred, CancelationType.Immediate }; + var completeTypes = new[] { CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel }; + var bools = new[] { true, false }; + var falseOnly = new[] { false }; + + foreach (var cancelationType in cancelationTypes) + foreach (var cancelOnNonResolved in bools) + foreach (var completeType1 in completeTypes) + foreach (var alreadyComplete1 in bools) + foreach (var completeType2 in completeTypes) + foreach (var alreadyComplete2 in bools) + foreach (var completeFirstPromiseFirst in !alreadyComplete1 && !alreadyComplete2 ? bools : falseOnly) + { + yield return new TestCaseData(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + } + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + var expectedValue = GetExpectedValue(0, 1, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(completeType1, completeType2); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!completeFirstPromiseFirst) + { + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + private static Promise.State GetExpectedState(CancelationType cancelationType, bool cancelOnNonResolved, + CompleteType completeType1, bool alreadyComplete1, CompleteType completeType2, bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + if (cancelationType == CancelationType.Immediate) + { + return Promise.State.Canceled; + } + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + if (alreadyComplete1) + { + if (completeType1 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + + if (cancelOnNonResolved) + { + return (Promise.State) completeType1; + } + + if (alreadyComplete2) + { + if (completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + if (completeType2 == CompleteType.Cancel) + { + return (Promise.State) completeType1; + } + return Promise.State.Rejected; + } + + if (cancelationType == CancelationType.Deferred) + { + return (Promise.State) completeType1; + } + if (completeType2 == CompleteType.Cancel) + { + return (Promise.State) completeType1; + } + return (Promise.State) completeType2; + } + + if (alreadyComplete2) + { + if (completeType2 == CompleteType.Resolve) + { + return Promise.State.Resolved; + } + + if (cancelOnNonResolved) + { + return (Promise.State) completeType2; + } + + if (completeType1 == CompleteType.Cancel) + { + return (Promise.State) completeType2; + } + return (Promise.State) completeType1; + } + + if (cancelationType == CancelationType.Deferred) + { + return (Promise.State) completeType1; + } + + // CancelationType.None + if (cancelOnNonResolved) + { + return (Promise.State) completeType1; + } + + return GetExpectedState(completeType1, completeType2); + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_WithCancelation_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + var expectedValue = GetExpectedValue(0, 1, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + var expectedState = GetExpectedState(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || (alreadyComplete1 && alreadyComplete2) + || (cancelOnNonResolved && (alreadyComplete1 || alreadyComplete2)) + || (alreadyComplete1 && completeType1 == CompleteType.Resolve), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceWithIndexGroup_NonResolved_CancelationCallbackExceptionsArePropagated_2_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_void(cancelationType, true, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + [Test] + public void PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_void( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values(CompleteType.Resolve)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_void(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + private static void Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_void( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + var cancelationAggregateException = e.InnerExceptions.OfType().Single(); + Assert.AreEqual(expectedExceptionCount - 1, e.InnerExceptions.OfType().Count()); + Assert.AreEqual(1, cancelationAggregateException.InnerExceptions.Count); + Assert.AreEqual(expectedException, cancelationAggregateException.InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceWithIndexGroup_UsingInvalidatedGroupThrows_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + var intPromise = Promise.Resolved(42); + Assert.Catch(() => default(PromiseRaceWithIndexGroup).Add(intPromise)); + Assert.Catch(() => default(PromiseRaceWithIndexGroup).WaitAsync()); + + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup1 = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + var raceGroup2 = raceGroup1.Add(Promise.Resolved(2)); + Assert.Catch(() => raceGroup1.Add(intPromise)); + Assert.Catch(() => raceGroup1.WaitAsync()); + + var raceGroup3 = raceGroup2.Add(Promise.Resolved(2)); + Assert.Catch(() => raceGroup2.Add(intPromise)); + Assert.Catch(() => raceGroup2.WaitAsync()); + + raceGroup3.WaitAsync().Forget(); + Assert.Catch(() => raceGroup3.Add(intPromise)); + Assert.Catch(() => raceGroup3.WaitAsync()); + + intPromise.Forget(); + } + } + + [Test] + public void PromiseRaceWithIndexGroupThrowsWhenNoPromisesAreAdded_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Assert.Catch(() => raceGroup.WaitAsync()); + + raceGroup.Add(Promise.Resolved(2)).WaitAsync().Forget(); + } + } + + [Test] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_1_T( + [Values] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values] CompleteType completeType, + [Values] bool alreadyComplete) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + Exception expectedException = new Exception("Bang!"); + + int value1 = 1; + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType, alreadyComplete, value1, expectedException, out var tryCompleter)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual((Promise.State) completeType, result.State); + if (completeType == CompleteType.Reject) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (completeType == CompleteType.Resolve) + { + Assert.AreEqual((0, value1), result.Value); + } + }) + .Forget(); + + Assert.AreEqual(alreadyComplete, completed); + + tryCompleter(); + Assert.IsTrue(completed); + } + } + + private static T GetExpectedValue(T value1, T value2, + CompleteType completeType1, bool alreadyComplete1, CompleteType completeType2, bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + (value1, value2) = (value2, value1); + } + + if (alreadyComplete1) + { + return completeType1 == CompleteType.Resolve + ? value1 + : value2; + } + + if (alreadyComplete2) + { + return completeType2 == CompleteType.Resolve + ? value2 + : value1; + } + + return completeType1 == CompleteType.Resolve + ? value1 + : value2; + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out _, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out _, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out _, cancelOnNonResolved); + + int value1 = 42; + int value2 = 101; + Exception expectedException = new Exception("Bang!"); + var expectedValue = GetExpectedValue((0, value1), (1, value2), completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + var expectedState = GetExpectedState(completeType1, completeType2); + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!completeFirstPromiseFirst) + { + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test, TestCaseSource(nameof(GetArgs))] + public void PromiseRaceWithIndexGroupAdoptsTheStateOfTheFirstCompletedPromise_WithCancelation_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2, + bool completeFirstPromiseFirst) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + int value1 = 42; + int value2 = 101; + Exception expectedException = new Exception("Bang!"); + var expectedValue = GetExpectedValue((0, value1), (1, value2), completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + var expectedState = GetExpectedState(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2, completeFirstPromiseFirst); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, value1, expectedException, groupCancelationToken, out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, value2, expectedException, groupCancelationToken, out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + + Assert.AreEqual(expectedState, result.State); + if (expectedState == Promise.State.Rejected) + { + Assert.IsAssignableFrom(result.Reason); + Assert.AreEqual(expectedException, result.Reason.UnsafeAs().InnerExceptions[0]); + } + else if (expectedState == Promise.State.Resolved) + { + Assert.AreEqual(expectedValue, result.Value); + } + }) + .Forget(); + + if (!alreadyComplete1 && (alreadyComplete2 || !completeFirstPromiseFirst)) + { + // If the second promise should complete first, swap them. + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); + (completeType1, completeType2) = (completeType2, completeType1); + (alreadyComplete1, alreadyComplete2) = (alreadyComplete2, alreadyComplete1); + } + + Assert.AreEqual(cancelationType == CancelationType.Immediate + || (alreadyComplete1 && alreadyComplete2) + || (cancelOnNonResolved && (alreadyComplete1 || alreadyComplete2)) + || (alreadyComplete1 && completeType1 == CompleteType.Resolve), + completed); + + tryCompleter1(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + cancelationSource.Cancel(); + Assert.AreEqual(cancelationType == CancelationType.Immediate + || cancelationType == CancelationType.Deferred + || alreadyComplete2 + || cancelOnNonResolved + || completeType1 == CompleteType.Resolve, + completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + + [Test] + public void PromiseRaceWithIndexGroup_NonResolved_CancelationCallbackExceptionsArePropagated_2_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_T(cancelationType, true, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + [Test] + public void PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_T( + [Values(CancelationType.None, CancelationType.Deferred)] CancelationType cancelationType, + [Values] bool cancelOnNonResolved, + [Values(CompleteType.Resolve)] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2) + { + Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_T(cancelationType, cancelOnNonResolved, completeType1, alreadyComplete1, completeType2, alreadyComplete2); + } + + private static void Test_PromiseRaceWithIndexGroup_CancelationCallbackExceptionsArePropagated_2_T( + CancelationType cancelationType, + bool cancelOnNonResolved, + CompleteType completeType1, + bool alreadyComplete1, + CompleteType completeType2, + bool alreadyComplete2) + { + using (var cancelationSource = CancelationSource.New()) + { + var raceGroup = cancelationType == CancelationType.None ? PromiseRaceWithIndexGroup.New(out var groupCancelationToken, cancelOnNonResolved) + : cancelationType == CancelationType.Deferred ? PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken, cancelOnNonResolved) + : PromiseRaceWithIndexGroup.New(CancelationToken.Canceled(), out groupCancelationToken, cancelOnNonResolved); + + Exception expectedException = new Exception("Error in cancelation!"); + groupCancelationToken.Register(() => { throw expectedException; }); + + bool completed = false; + + raceGroup + .Add(TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, new System.InvalidOperationException("Bang!"), out var tryCompleter1)) + .Add(TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, new System.InvalidOperationException("Bang!"), out var tryCompleter2)) + .WaitAsync() + .ContinueWith(result => + { + completed = true; + Assert.AreEqual(Promise.State.Rejected, result.State); + Assert.IsAssignableFrom(result.Reason); + + int expectedExceptionCount = 1; + if (completeType1 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + if (completeType2 == CompleteType.Reject) + { + ++expectedExceptionCount; + } + + var e = result.Reason.UnsafeAs(); + Assert.AreEqual(expectedExceptionCount, e.InnerExceptions.Count); + var cancelationAggregateException = e.InnerExceptions.OfType().Single(); + Assert.AreEqual(expectedExceptionCount - 1, e.InnerExceptions.OfType().Count()); + Assert.AreEqual(1, cancelationAggregateException.InnerExceptions.Count); + Assert.AreEqual(expectedException, cancelationAggregateException.InnerExceptions[0]); + }) + .Forget(); + + Assert.AreEqual(alreadyComplete1 && alreadyComplete2, completed); + + tryCompleter1(); + Assert.AreEqual(alreadyComplete2, completed); + + tryCompleter2(); + Assert.IsTrue(completed); + } + } + } +} \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs.meta b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs.meta new file mode 100644 index 00000000..da27f766 --- /dev/null +++ b/Package/Tests/CoreTests/APIs/PromiseGroups/PromiseRaceWithIndexGroupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 09c2fab4e87e69449aeacd5453511168 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/APIs/RaceTests.cs b/Package/Tests/CoreTests/APIs/RaceTests.cs index 33896500..c52dcf82 100644 --- a/Package/Tests/CoreTests/APIs/RaceTests.cs +++ b/Package/Tests/CoreTests/APIs/RaceTests.cs @@ -27,8 +27,8 @@ public void Teardown() public void RaceIsResolvedWhenFirstPromiseIsResolvedFirst_void( [Values] bool alreadyResolved) { - var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred1, out _); - var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter1); + var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -39,11 +39,11 @@ public void RaceIsResolvedWhenFirstPromiseIsResolvedFirst_void( }) .Forget(); - deferred1.TryResolve(); + tryCompleter1(); Assert.IsTrue(resolved); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -52,8 +52,8 @@ public void RaceIsResolvedWhenFirstPromiseIsResolvedFirst_void( public void RaceIsResolvedWhenFirstPromiseIsResolvedFirst_T( [Values] bool alreadyResolved) { - var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred1, out _); - var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 1, "Error", out var deferred2, out _); + var resolvedPromise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter1); + var resolvedPromise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 1, "Error", out var tryCompleter2); bool resolved = false; @@ -65,11 +65,11 @@ public void RaceIsResolvedWhenFirstPromiseIsResolvedFirst_T( }) .Forget(); - deferred1.TryResolve(5); + tryCompleter1(); Assert.IsTrue(resolved); - deferred2.TryResolve(1); + tryCompleter2(); Assert.IsTrue(resolved); } @@ -79,7 +79,7 @@ public void RaceIsResolvedWhenSecondPromiseIsResolvedFirst_void( [Values] bool alreadyResolved) { var deferred1 = Promise.NewDeferred(); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var deferred2, out _); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, "Error", out var tryCompleter2); bool resolved = false; @@ -90,7 +90,7 @@ public void RaceIsResolvedWhenSecondPromiseIsResolvedFirst_void( }) .Forget(); - deferred2.TryResolve(); + tryCompleter2(); Assert.IsTrue(resolved); @@ -104,7 +104,7 @@ public void RaceIsResolvedWhenSecondPromiseIsResolvedFirst_T( [Values] bool alreadyResolved) { var deferred1 = Promise.NewDeferred(); - var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var deferred2, out _); + var resolvedPromise = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved, 5, "Error", out var tryCompleter2); bool resolved = false; @@ -116,7 +116,7 @@ public void RaceIsResolvedWhenSecondPromiseIsResolvedFirst_T( }) .Forget(); - deferred2.TryResolve(5); + tryCompleter2(); Assert.IsTrue(resolved); @@ -129,11 +129,11 @@ public void RaceIsResolvedWhenSecondPromiseIsResolvedFirst_T( public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_void( [Values] bool alreadyRejected) { - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, "Error", out var deferred1, out _); + string expected = "Error"; + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, expected, out var tryCompleter1); var deferred2 = Promise.NewDeferred(); bool invoked = false; - string expected = "Error"; Promise.Race(rejectPromise, deferred2.Promise) .Catch((string rej) => @@ -143,7 +143,7 @@ public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_void( }) .Forget(); - deferred1.TryReject(expected); + tryCompleter1(); Assert.IsTrue(invoked); @@ -156,11 +156,11 @@ public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_void( public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_T( [Values] bool alreadyRejected) { - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var deferred1, out _); + string expected = "Error"; + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, expected, out var tryCompleter1); var deferred2 = Promise.NewDeferred(); bool invoked = false; - string expected = "Error"; Promise.Race(rejectPromise, deferred2.Promise) .Catch((string rej) => @@ -170,7 +170,7 @@ public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_T( }) .Forget(); - deferred1.TryReject(expected); + tryCompleter1(); Assert.IsTrue(invoked); @@ -183,11 +183,11 @@ public void RaceIsRejectedWhenFirstPromiseIsRejectedFirst_T( public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_void( [Values] bool alreadyRejected) { + string expected = "Error"; var deferred1 = Promise.NewDeferred(); - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, expected, out var tryCompleter2); bool invoked = false; - string expected = "Error"; Promise.Race(deferred1.Promise, rejectPromise) .Catch((string rej) => @@ -197,7 +197,7 @@ public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_void( }) .Forget(); - deferred2.TryReject(expected); + tryCompleter2(); Assert.IsTrue(invoked); @@ -210,11 +210,11 @@ public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_void( public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_T( [Values] bool alreadyRejected) { + string expected = "Error"; var deferred1 = Promise.NewDeferred(); - var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, "Error", out var deferred2, out _); + var rejectPromise = TestHelper.BuildPromise(CompleteType.Reject, alreadyRejected, 5, expected, out var tryCompleter2); bool invoked = false; - string expected = "Error"; Promise.Race(deferred1.Promise, rejectPromise) .Catch((string rej) => @@ -224,7 +224,7 @@ public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_T( }) .Forget(); - deferred2.TryReject(expected); + tryCompleter2(); Assert.IsTrue(invoked); @@ -237,7 +237,7 @@ public void RaceIsRejectedWhenSecondPromiseIsRejectedFirst_T( public void RaceIsCanceledWhenFirstPromiseIsCanceledFirst_void( [Values] bool alreadyCanceled) { - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred1, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter); var deferred2 = Promise.NewDeferred(); bool invoked = false; @@ -249,7 +249,7 @@ public void RaceIsCanceledWhenFirstPromiseIsCanceledFirst_void( }) .Forget(); - deferred1.TryCancel(); + tryCompleter(); Assert.IsTrue(invoked); @@ -262,7 +262,7 @@ public void RaceIsCanceledWhenFirstPromiseIsCanceledFirst_void( public void RaceIsCanceledWhenFirstPromiseIsCanceledFirst_T( [Values] bool alreadyCanceled) { - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var deferred1, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var tryCompleter1); var deferred2 = Promise.NewDeferred(); bool invoked = false; @@ -274,7 +274,7 @@ public void RaceIsCanceledWhenFirstPromiseIsCanceledFirst_T( }) .Forget(); - deferred1.TryCancel(); + tryCompleter1(); Assert.IsTrue(invoked); @@ -288,7 +288,7 @@ public void RaceIsCanceledWhenSecondPromiseIsCanceledFirst_void( [Values] bool alreadyCanceled) { var deferred1 = Promise.NewDeferred(); - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, "Error", out var tryCompleter2); bool invoked = false; @@ -299,7 +299,7 @@ public void RaceIsCanceledWhenSecondPromiseIsCanceledFirst_void( }) .Forget(); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsTrue(invoked); @@ -313,7 +313,7 @@ public void RaceIsCanceledWhenSecondPromiseIsCanceledFirst_T( [Values] bool alreadyCanceled) { var deferred1 = Promise.NewDeferred(); - var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var deferred2, out _); + var cancelPromise = TestHelper.BuildPromise(CompleteType.Cancel, alreadyCanceled, 5, "Error", out var tryCompleter2); bool invoked = false; @@ -324,7 +324,7 @@ public void RaceIsCanceledWhenSecondPromiseIsCanceledFirst_T( }) .Forget(); - deferred2.TryCancel(); + tryCompleter2(); Assert.IsTrue(invoked); @@ -333,20 +333,13 @@ public void RaceIsCanceledWhenSecondPromiseIsCanceledFirst_T( Assert.IsTrue(invoked); } - private static void Swap(ref T a, ref T b) - { - var temp = a; - a = b; - b = temp; - } - [Test] public void RaceWithIndex_2_void( [Values(0, 1)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); int resultIndex = -1; @@ -356,10 +349,10 @@ public void RaceWithIndex_2_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); + tryCompleter1(); + tryCompleter2(); Assert.AreEqual(winIndex, resultIndex); } @@ -369,9 +362,9 @@ public void RaceWithIndex_3_void( [Values(0, 1, 2)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); int resultIndex = -1; @@ -381,15 +374,15 @@ public void RaceWithIndex_3_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); Assert.AreEqual(winIndex, resultIndex); } @@ -399,10 +392,10 @@ public void RaceWithIndex_4_void( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var tryCompleter4); int resultIndex = -1; @@ -412,20 +405,20 @@ public void RaceWithIndex_4_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); - deferred4.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); } @@ -435,10 +428,10 @@ public void RaceWithIndex_array_void( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, "Error", out var tryCompleter4); int resultIndex = -1; @@ -448,20 +441,20 @@ public void RaceWithIndex_array_void( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(); - deferred2.TryResolve(); - deferred3.TryResolve(); - deferred4.TryResolve(); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); } @@ -471,8 +464,8 @@ public void RaceWithIndex_2_T( [Values(0, 1)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); int resultIndex = -1; int result = -1; @@ -487,10 +480,10 @@ public void RaceWithIndex_2_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); + tryCompleter1(); + tryCompleter2(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -501,9 +494,9 @@ public void RaceWithIndex_3_T( [Values(0, 1, 2)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); int resultIndex = -1; int result = -1; @@ -518,15 +511,15 @@ public void RaceWithIndex_3_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -537,10 +530,10 @@ public void RaceWithIndex_4_T( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, alreadyResolved && winIndex == 3 ? 1 : 4, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, winIndex == 3 ? 1 : 4, "Error", out var tryCompleter4); int resultIndex = -1; int result = -1; @@ -555,20 +548,20 @@ public void RaceWithIndex_4_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); - deferred4.TryResolve(4); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); @@ -579,10 +572,10 @@ public void RaceWithIndex_array_T( [Values(0, 1, 2, 3)] int winIndex, [Values] bool alreadyResolved) { - var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, alreadyResolved && winIndex == 0 ? 1 : 0, "Error", out var deferred1, out _); - var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, alreadyResolved && winIndex == 1 ? 1 : 2, "Error", out var deferred2, out _); - var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, alreadyResolved && winIndex == 2 ? 1 : 3, "Error", out var deferred3, out _); - var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, alreadyResolved && winIndex == 3 ? 1 : 4, "Error", out var deferred4, out _); + var promise1 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 0, winIndex == 0 ? 1 : 0, "Error", out var tryCompleter1); + var promise2 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 1, winIndex == 1 ? 1 : 2, "Error", out var tryCompleter2); + var promise3 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 2, winIndex == 2 ? 1 : 3, "Error", out var tryCompleter3); + var promise4 = TestHelper.BuildPromise(CompleteType.Resolve, alreadyResolved && winIndex == 3, winIndex == 3 ? 1 : 4, "Error", out var tryCompleter4); int resultIndex = -1; int result = -1; @@ -597,20 +590,20 @@ public void RaceWithIndex_array_T( if (winIndex == 1) { - Swap(ref deferred1, ref deferred2); + (tryCompleter1, tryCompleter2) = (tryCompleter2, tryCompleter1); } else if (winIndex == 2) { - Swap(ref deferred1, ref deferred3); + (tryCompleter1, tryCompleter3) = (tryCompleter3, tryCompleter1); } else if (winIndex == 3) { - Swap(ref deferred1, ref deferred4); + (tryCompleter1, tryCompleter4) = (tryCompleter4, tryCompleter1); } - deferred1.TryResolve(1); - deferred2.TryResolve(2); - deferred3.TryResolve(3); - deferred4.TryResolve(4); + tryCompleter1(); + tryCompleter2(); + tryCompleter3(); + tryCompleter4(); Assert.AreEqual(winIndex, resultIndex); Assert.AreEqual(1, result); diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncAutoResetEventTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncAutoResetEventTests.cs index 1ed3b418..d9f57911 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncAutoResetEventTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncAutoResetEventTests.cs @@ -102,7 +102,7 @@ public void AsyncAutoResetEvent_CancelAfterWait() Promise.Run(() => { - SpinWait.SpinUntil(() => isAboutToWait); + TestHelper.SpinUntil(() => isAboutToWait, TimeSpan.FromSeconds(1)); Thread.Sleep(TimeSpan.FromSeconds(0.5f)); cs.Cancel(); }, SynchronizationOption.Background) diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncCountdownEventTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncCountdownEventTests.cs index 6340dd31..515286e1 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncCountdownEventTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncCountdownEventTests.cs @@ -255,7 +255,7 @@ public void AsyncCountdownEvent_CancelAfterWait() Promise.Run(() => { - SpinWait.SpinUntil(() => isAboutToWait); + TestHelper.SpinUntil(() => isAboutToWait, TimeSpan.FromSeconds(1)); Thread.Sleep(TimeSpan.FromSeconds(0.5f)); cs.Cancel(); }, SynchronizationOption.Background) diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncLockTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncLockTests.cs index 4a0eeb2f..2403d7a8 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncLockTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncLockTests.cs @@ -205,7 +205,7 @@ public void AsyncLock_Locked_PreventsLockUntilUnlocked_Then() }) .Forget(); - SpinWait.SpinUntil(() => isLocked); + TestHelper.SpinUntil(() => isLocked, TimeSpan.FromSeconds(1)); bool promise2IsComplete = false; var promise2 = Promise.Run(() => @@ -244,7 +244,7 @@ public void AsyncLock_Locked_OnlyPermitsOneLockerAtATime_Then() }) .Forget(); - SpinWait.SpinUntil(() => isLocked); + TestHelper.SpinUntil(() => isLocked, TimeSpan.FromSeconds(1)); bool deferred2Ready = false; bool deferred2HasLock = false; @@ -262,7 +262,7 @@ public void AsyncLock_Locked_OnlyPermitsOneLockerAtATime_Then() .ContinueWith(r => r.Value.Dispose()); }); - SpinWait.SpinUntil(() => deferred2Ready); + TestHelper.SpinUntil(() => deferred2Ready, TimeSpan.FromSeconds(1)); bool promise3Complete = false; var promise3 = Promise.Run(() => @@ -274,7 +274,7 @@ public void AsyncLock_Locked_OnlyPermitsOneLockerAtATime_Then() deferred1Continue.Resolve(); - SpinWait.SpinUntil(() => deferred2HasLock); + TestHelper.SpinUntil(() => deferred2HasLock, TimeSpan.FromSeconds(1)); Assert.IsFalse(promise3Complete); deferred2Continue.Resolve(); @@ -300,7 +300,7 @@ public void AsyncLock_CanceledLock_LeavesLockUnlocked_Then() return lockPromise; }); - SpinWait.SpinUntil(() => triedToEnterLock); + TestHelper.SpinUntil(() => triedToEnterLock, TimeSpan.FromSeconds(1)); cts.Cancel(); Assert.Catch(() => promise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1))); @@ -511,7 +511,7 @@ public void AsyncLock_CanceledLock_LeavesLockUnlocked_AsyncAwait() await lockPromise; }); - SpinWait.SpinUntil(() => triedToEnterLock); + TestHelper.SpinUntil(() => triedToEnterLock, TimeSpan.FromSeconds(1)); cts.Cancel(); Assert.Catch(() => promise.WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1))); diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncManualResetEventTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncManualResetEventTests.cs index 89b25a57..dfd31032 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncManualResetEventTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncManualResetEventTests.cs @@ -106,7 +106,7 @@ public void AsyncManualResetEvent_CancelAfterWait() Promise.Run(() => { - SpinWait.SpinUntil(() => isAboutToWait); + TestHelper.SpinUntil(() => isAboutToWait, TimeSpan.FromSeconds(1)); Thread.Sleep(TimeSpan.FromSeconds(0.5f)); cs.Cancel(); }, SynchronizationOption.Background) diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncMonitorTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncMonitorTests.cs index b5c3aca2..162b685f 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncMonitorTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncMonitorTests.cs @@ -331,7 +331,7 @@ public void AsyncMonitor_TryEnter_ReturnsFalseIfLockIsHeld( Thread.Sleep(100); cancelationSource.Cancel(); // Make sure we don't exit the lock until the other thread returned from TryEnter. - SpinWait.SpinUntil(() => didWait); + TestHelper.SpinUntil(() => didWait, TimeSpan.FromSeconds(1)); } }); @@ -424,7 +424,7 @@ public void AsyncMonitor_TryEnter_ReturnsFalseIfTokenIsCanceledWhileLockIsHeld() Thread.Sleep(100); cancelationSource.Cancel(); // Make sure we don't exit the lock until the other thread returned from TryEnter. - SpinWait.SpinUntil(() => didWait); + TestHelper.SpinUntil(() => didWait, TimeSpan.FromSeconds(1)); } }); diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncReaderWriterLockTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncReaderWriterLockTests.cs index c5c444b6..3baca6ad 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncReaderWriterLockTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncReaderWriterLockTests.cs @@ -1684,13 +1684,13 @@ public void AsyncReaderWriterLock_UpgradeableReaderLocksTakeLockInOrder_AfterNor { var rwl = new AsyncReaderWriterLock(); var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); + var promiseRetainer = deferred.Promise.GetRetainer(); Promise.Run(async () => { using (await rwl.WriterLockAsync()) { - await promise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -1702,7 +1702,7 @@ public void AsyncReaderWriterLock_UpgradeableReaderLocksTakeLockInOrder_AfterNor using (await rwl.ReaderLockAsync()) { enteredReadLock = true; - await promise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -1715,7 +1715,7 @@ public void AsyncReaderWriterLock_UpgradeableReaderLocksTakeLockInOrder_AfterNor using (await rwl.UpgradeableReaderLockAsync()) { enteredFirstUpgradeableReadLock = true; - await promise; + await promiseRetainer; exitedFirstUpgradeableReadLock = true; } }, SynchronizationOption.Synchronous) @@ -1726,8 +1726,8 @@ public void AsyncReaderWriterLock_UpgradeableReaderLocksTakeLockInOrder_AfterNor var d = deferred; deferred = Promise.NewDeferred(); - promise.Forget(); - promise = deferred.Promise.Preserve(); + promiseRetainer.Dispose(); + promiseRetainer = deferred.Promise.GetRetainer(); d.Resolve(); // Continuations are posted asynchronously to the current context, so we need to make sure it is executed. TestHelper.ExecuteForegroundCallbacks(); @@ -1751,7 +1751,7 @@ public void AsyncReaderWriterLock_UpgradeableReaderLocksTakeLockInOrder_AfterNor Assert.False(enteredSecondUpgradeableReadLock); deferred.Resolve(); - promise.Forget(); + promiseRetainer.Dispose(); // Continuations are posted asynchronously to the current context, so we need to make sure it is executed. TestHelper.ExecuteForegroundCallbacks(); } @@ -2226,7 +2226,7 @@ public void AsyncReaderWriterLock_ReleaseWriterLock_ResolvesReaderLockAndUpgrade var cts = CancelationSource.New(); var writerDeferred = Promise.NewDeferred(); var readersDeferred = Promise.NewDeferred(); - var preservedPromise = readersDeferred.Promise.Preserve(); + var promiseRetainer = readersDeferred.Promise.GetRetainer(); Promise.Run(async () => { @@ -2245,7 +2245,7 @@ public void AsyncReaderWriterLock_ReleaseWriterLock_ResolvesReaderLockAndUpgrade using (await rwl.UpgradeableReaderLockAsync()) { isUpgradeableReaderLocked = true; - await preservedPromise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -2255,7 +2255,7 @@ public void AsyncReaderWriterLock_ReleaseWriterLock_ResolvesReaderLockAndUpgrade using (await rwl.ReaderLockAsync()) { isReaderLocked = true; - await preservedPromise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -2272,7 +2272,7 @@ public void AsyncReaderWriterLock_ReleaseWriterLock_ResolvesReaderLockAndUpgrade readersDeferred.Resolve(); TestHelper.ExecuteForegroundCallbacks(); - preservedPromise.Forget(); + promiseRetainer.Dispose(); cts.Dispose(); } @@ -2283,7 +2283,7 @@ public void AsyncReaderWriterLock_ReleaseUpgradedWriterLock_ResolvesReaderLockOn var cts = CancelationSource.New(); var writerDeferred = Promise.NewDeferred(); var readersDeferred = Promise.NewDeferred(); - var preservedPromise = readersDeferred.Promise.Preserve(); + var promiseRetainer = readersDeferred.Promise.GetRetainer(); Promise.Run(async () => { @@ -2293,7 +2293,7 @@ public void AsyncReaderWriterLock_ReleaseUpgradedWriterLock_ResolvesReaderLockOn { await writerDeferred.Promise; } - await preservedPromise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -2306,7 +2306,7 @@ public void AsyncReaderWriterLock_ReleaseUpgradedWriterLock_ResolvesReaderLockOn using (await rwl.UpgradeableReaderLockAsync()) { isUpgradeableReaderLocked = true; - await preservedPromise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -2316,7 +2316,7 @@ public void AsyncReaderWriterLock_ReleaseUpgradedWriterLock_ResolvesReaderLockOn using (await rwl.ReaderLockAsync()) { isReaderLocked = true; - await preservedPromise; + await promiseRetainer; } }, SynchronizationOption.Synchronous) .Forget(); @@ -2333,7 +2333,7 @@ public void AsyncReaderWriterLock_ReleaseUpgradedWriterLock_ResolvesReaderLockOn readersDeferred.Resolve(); TestHelper.ExecuteForegroundCallbacks(); - preservedPromise.Forget(); + promiseRetainer.Dispose(); cts.Dispose(); } @@ -2588,7 +2588,8 @@ public void AsyncReaderWriterLock_LocksDoNotStarveOtherLocks_Async( var readerReadyDeferred = Promise.NewDeferred(); var writerReadyDeferred = Promise.NewDeferred(); var upgradeableReaderReadyDeferred = Promise.NewDeferred(); - var allReadyPromise = Promise.All(readerReadyDeferred.Promise, writerReadyDeferred.Promise, upgradeableReaderReadyDeferred.Promise).Preserve(); + var allReadyPromiseRetainer = Promise.All(readerReadyDeferred.Promise, writerReadyDeferred.Promise, upgradeableReaderReadyDeferred.Promise) + .GetRetainer(); var readerRunner = readerStartDeferred.Promise .WaitAsync(runnerOption) @@ -2598,7 +2599,7 @@ public void AsyncReaderWriterLock_LocksDoNotStarveOtherLocks_Async( var lockPromise = rwl.ReaderLockAsync(); readerReadyDeferred.Resolve(); - await allReadyPromise.WaitAsync(runnerOption); // Wait for the other runners to start. + await allReadyPromiseRetainer.WaitAsync().WaitAsync(runnerOption); // Wait for the other runners to start. while (readerCount < expectedCounts || writerCount < expectedCounts || upgradeableReaderCount < expectedCounts || upgradedWriterCount < expectedCounts) { @@ -2620,7 +2621,7 @@ public void AsyncReaderWriterLock_LocksDoNotStarveOtherLocks_Async( var lockPromise = rwl.WriterLockAsync(); writerReadyDeferred.Resolve(); - await allReadyPromise.WaitAsync(runnerOption); // Wait for the other runners to start. + await allReadyPromiseRetainer.WaitAsync().WaitAsync(runnerOption); // Wait for the other runners to start. while (readerCount < expectedCounts || writerCount < expectedCounts || upgradeableReaderCount < expectedCounts || upgradedWriterCount < expectedCounts) { @@ -2642,7 +2643,7 @@ public void AsyncReaderWriterLock_LocksDoNotStarveOtherLocks_Async( var lockPromise = rwl.UpgradeableReaderLockAsync(); upgradeableReaderReadyDeferred.Resolve(); - await allReadyPromise.WaitAsync(runnerOption); // Wait for the other runners to start. + await allReadyPromiseRetainer.WaitAsync().WaitAsync(runnerOption); // Wait for the other runners to start. while (readerCount < expectedCounts || writerCount < expectedCounts || upgradeableReaderCount < expectedCounts || upgradedWriterCount < expectedCounts) { @@ -2677,7 +2678,7 @@ public void AsyncReaderWriterLock_LocksDoNotStarveOtherLocks_Async( Promise.All(readerRunner, writerRunner, upgradeableReaderRunner) .WaitWithTimeoutWhileExecutingForegroundContext(TimeSpan.FromSeconds(1)); - allReadyPromise.Forget(); + allReadyPromiseRetainer.Dispose(); Assert.GreaterOrEqual(readerCount, expectedCounts); Assert.GreaterOrEqual(writerCount, expectedCounts); diff --git a/Package/Tests/CoreTests/APIs/Threading/AsyncSemaphoreTests.cs b/Package/Tests/CoreTests/APIs/Threading/AsyncSemaphoreTests.cs index e866afb7..d65dda3c 100644 --- a/Package/Tests/CoreTests/APIs/Threading/AsyncSemaphoreTests.cs +++ b/Package/Tests/CoreTests/APIs/Threading/AsyncSemaphoreTests.cs @@ -449,7 +449,7 @@ public void AsyncSemaphore_TryWait_Canceled_DoesNotTakeSlot() return semaphore.TryWait(cancelationSource.Token); }, SynchronizationOption.Background, forceAsync: true); - SpinWait.SpinUntil(() => ready); + TestHelper.SpinUntil(() => ready, TimeSpan.FromSeconds(1)); Thread.Sleep(10); cancelationSource.Cancel(); @@ -485,21 +485,15 @@ public void AsyncSemaphore_Release_WithWaiters_Releases1Waiter_Sync() }, SynchronizationOption.Background, forceAsync: true) .Forget(); - SpinWait.SpinUntil(() => readyCount == 2); + TestHelper.SpinUntil(() => readyCount == 2, TimeSpan.FromSeconds(1)); Thread.Sleep(10); Assert.AreEqual(0, completeCount); semaphore.Release(); // It's a threading race condition, we can't know which one queued up first. - if (!SpinWait.SpinUntil(() => completeCount == 1, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntil(() => completeCount == 1, TimeSpan.FromSeconds(1)); semaphore.Release(); - if (!SpinWait.SpinUntil(() => completeCount == 2, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntil(() => completeCount == 2, TimeSpan.FromSeconds(1)); cancelationSource.Dispose(); } @@ -542,7 +536,7 @@ public void AsyncSemaphore_ReleaseMultiple_WithWaiters_ReleasesWaitersCount_Sync }, SynchronizationOption.Background, forceAsync: true) .Forget(); - SpinWait.SpinUntil(() => readyCount == 4); + TestHelper.SpinUntil(() => readyCount == 4, TimeSpan.FromSeconds(4)); Thread.Sleep(10); Assert.AreEqual(0, semaphore.CurrentCount); Assert.AreEqual(0, completeCount); @@ -550,22 +544,13 @@ public void AsyncSemaphore_ReleaseMultiple_WithWaiters_ReleasesWaitersCount_Sync semaphore.Release(2); Assert.AreEqual(0, semaphore.CurrentCount); // It's a threading race condition, we can't know which ones queued up first. - if (!SpinWait.SpinUntil(() => completeCount == 2, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntil(() => completeCount == 2, TimeSpan.FromSeconds(1)); semaphore.Release(1); Assert.AreEqual(0, semaphore.CurrentCount); - if (!SpinWait.SpinUntil(() => completeCount == 3, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntil(() => completeCount == 3, TimeSpan.FromSeconds(1)); semaphore.Release(4); Assert.AreEqual(3, semaphore.CurrentCount); - if (!SpinWait.SpinUntil(() => completeCount == 4, TimeSpan.FromSeconds(1))) - { - throw new TimeoutException(); - } + TestHelper.SpinUntil(() => completeCount == 4, TimeSpan.FromSeconds(1)); cancelationSource.Dispose(); } diff --git a/Package/Tests/CoreTests/APIs/UncaughtRejectionTests.cs b/Package/Tests/CoreTests/APIs/UncaughtRejectionTests.cs index e0a5abe8..61b894ec 100644 --- a/Package/Tests/CoreTests/APIs/UncaughtRejectionTests.cs +++ b/Package/Tests/CoreTests/APIs/UncaughtRejectionTests.cs @@ -37,7 +37,7 @@ private static IEnumerable GetExpectedRejections() yield return new TestCaseData(42); } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -95,18 +95,21 @@ public void UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expect ); var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - foreach (var callback in actions) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + foreach (var callback in actions) { - ++expectedCount; - callback.Invoke(promise); + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + callback.Invoke(promise); + } } } - preservedPromise.Forget(); +#if !PROMISE_DEBUG + expectedCount -= 40; +#endif deferred.Reject(expectedRejectionValue); cancelationSource.Dispose(); Assert.AreEqual(expectedCount, uncaughtCount); @@ -117,7 +120,7 @@ public void UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expect } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -175,18 +178,21 @@ public void UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedR ); var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - foreach (var callback in actions) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + foreach (var callback in actions) { - ++expectedCount; - callback.Invoke(promise); + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + callback.Invoke(promise); + } } } - preservedPromise.Forget(); +#if !PROMISE_DEBUG + expectedCount -= 40; +#endif deferred.Reject(expectedRejectionValue); cancelationSource.Dispose(); Assert.AreEqual(expectedCount, uncaughtCount); @@ -197,7 +203,15 @@ public void UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedR } } - [Test, TestCaseSource("GetExpectedRejections")] + // In RELEASE mode, PromiseRetainer is used directly, and Duplicate returns itself. + // In DEBUG mode, a duplicate promise backing reference is used for each WaitAsync. +#if PROMISE_DEBUG + private const int expectedSubtraction = 1; +#else + private const int expectedSubtraction = 2; +#endif + + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expectedRejectionValue) { // Testing an implementation detail - when a promise is canceled and the previous promise is rejected, it counts as an uncaught rejection. @@ -238,20 +252,20 @@ public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHand .SelectMany(x => x); var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - foreach (var callback in actions) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - // We subtract 1 because the preserved promise will only report its unhandled rejection if none of the waiters suppress it. (In this case, the .Duplicate() does suppress it.) - --expectedCount; - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + foreach (var callback in actions) { - ++expectedCount; - callback.Invoke(promise, cancelationSource.Token).Forget(); + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + callback.Invoke(promise, cancelationSource.Token).Forget(); + } } } - preservedPromise.Forget(); cancelationSource.Cancel(); cancelationSource.Dispose(); deferred.Reject(expectedRejectionValue); @@ -264,7 +278,7 @@ public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHand } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedRejectionValue) { // Testing an implementation detail - when a promise is canceled and the previous promise is rejected, it counts as an uncaught rejection. @@ -305,20 +319,19 @@ public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHand .SelectMany(x => x); var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - foreach (var callback in actions) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - // We subtract 1 because the preserved promise will only report its unhandled rejection if none of the waiters suppress it. (In this case, the .Duplicate() does suppress it.) - --expectedCount; - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + foreach (var callback in actions) { - ++expectedCount; - callback.Invoke(promise, cancelationSource.Token).Forget(); + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + callback.Invoke(promise, cancelationSource.Token).Forget(); + } } } - - preservedPromise.Forget(); cancelationSource.Cancel(); cancelationSource.Dispose(); deferred.Reject(expectedRejectionValue); @@ -331,7 +344,7 @@ public void WhenPromiseIsCanceled_UncaughtRejectionIsSentToUncaughtRejectionHand } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -356,30 +369,30 @@ public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(ob var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.All(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.All(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.All(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.All(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -390,7 +403,7 @@ public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(ob } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -415,30 +428,30 @@ public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(objec var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.All(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.All(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.All(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.All(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -449,7 +462,7 @@ public void PromiseAll_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(objec } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseMerge_UncaughtRejectionIsSentToUncaughtRejectionHandler(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -474,30 +487,30 @@ public void PromiseMerge_UncaughtRejectionIsSentToUncaughtRejectionHandler(objec var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.Merge(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.Merge(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.Merge(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.Merge(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -508,7 +521,7 @@ public void PromiseMerge_UncaughtRejectionIsSentToUncaughtRejectionHandler(objec } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -533,30 +546,30 @@ public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(o var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.Race(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.Race(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.Race(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.Race(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -567,7 +580,7 @@ public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(o } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -592,30 +605,30 @@ public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(obje var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.Race(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.Race(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.Race(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.Race(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -626,7 +639,7 @@ public void PromiseRace_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(obje } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandler_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -651,30 +664,30 @@ public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandl var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.RaceWithIndex(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.RaceWithIndex(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.RaceWithIndex(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.RaceWithIndex(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -685,7 +698,7 @@ public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandl } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandler_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -710,30 +723,30 @@ public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandl var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - --expectedCount; - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - ++expectedCount; - Promise.RaceWithIndex(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - ++expectedCount; - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.RaceWithIndex(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + // Subtract expected because the other testable promises suppress the preserved/retained promise's rejection. + expectedCount -= expectedSubtraction; + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + ++expectedCount; + Promise.RaceWithIndex(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + ++expectedCount; + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.RaceWithIndex(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); @@ -744,7 +757,7 @@ public void PromiseRaceWithIndex_UncaughtRejectionIsSentToUncaughtRejectionHandl } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseFirst_UncaughtRejectionIsSuppressed_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -761,27 +774,26 @@ public void PromiseFirst_UncaughtRejectionIsSuppressed_void(object expectedRejec var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - Promise.First(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.First(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + Promise.First(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.First(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(0, uncaughtCount); @@ -792,7 +804,7 @@ public void PromiseFirst_UncaughtRejectionIsSuppressed_void(object expectedRejec } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseFirst_UncaughtRejectionIsSuppressed_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -809,27 +821,26 @@ public void PromiseFirst_UncaughtRejectionIsSuppressed_T(object expectedRejectio var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - Promise.First(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.First(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + Promise.First(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.First(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(0, uncaughtCount); @@ -840,7 +851,7 @@ public void PromiseFirst_UncaughtRejectionIsSuppressed_T(object expectedRejectio } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseFirstWithIndex_UncaughtRejectionIsSuppressed_void(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -857,27 +868,26 @@ public void PromiseFirstWithIndex_UncaughtRejectionIsSuppressed_void(object expe var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - Promise.FirstWithIndex(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.FirstWithIndex(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + Promise.FirstWithIndex(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.FirstWithIndex(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(0, uncaughtCount); @@ -888,7 +898,7 @@ public void PromiseFirstWithIndex_UncaughtRejectionIsSuppressed_void(object expe } } - [Test, TestCaseSource("GetExpectedRejections")] + [Test, TestCaseSource(nameof(GetExpectedRejections))] public void PromiseFirstWithIndex_UncaughtRejectionIsSuppressed_T(object expectedRejectionValue) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -905,27 +915,26 @@ public void PromiseFirstWithIndex_UncaughtRejectionIsSuppressed_T(object expecte var deferred1 = Promise.NewDeferred(); var deferred2 = Promise.NewDeferred(); - var preservedPromise1 = deferred1.Promise.Preserve(); - var preservedPromise2 = deferred2.Promise.Preserve(); - - // Subtract 1 because the other testable promises suppress the preserved promise's rejection. - foreach (var promise2 in TestHelper.GetTestablePromises(preservedPromise2)) + using (var promiseRetainer1 = deferred1.Promise.GetRetainer()) { - Promise.FirstWithIndex(preservedPromise1, promise2) - .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. - .Forget(); - } - - // Run it again with a freshly preserved promise that isn't suppressed. - var secondPreservedPromise2 = preservedPromise2.Preserve(); - preservedPromise2.Forget(); - - Promise.FirstWithIndex(preservedPromise1, secondPreservedPromise2) - .Catch(() => { }) - .Forget(); + using (var promiseRetainer2 = deferred2.Promise.GetRetainer()) + { + foreach (var promise2 in TestHelper.GetTestablePromises(promiseRetainer2)) + { + Promise.FirstWithIndex(promiseRetainer1.WaitAsync(), promise2) + .Catch(() => { }) // We catch the first rejection, the second will be reported as uncaught. + .Forget(); + } - preservedPromise1.Forget(); - secondPreservedPromise2.Forget(); + // Run it again with a freshly retained promise that isn't suppressed. + using (var secondPromiseRetainer2 = promiseRetainer2.WaitAsync().GetRetainer()) + { + Promise.FirstWithIndex(promiseRetainer1.WaitAsync(), secondPromiseRetainer2.WaitAsync()) + .Catch(() => { }) + .Forget(); + } + } + } deferred1.Reject(expectedRejectionValue); deferred2.Reject(expectedRejectionValue); Assert.AreEqual(0, uncaughtCount); @@ -955,7 +964,7 @@ private static IEnumerable GetExpectedRejectionsAndTimeout() } } - [Test, TestCaseSource("GetExpectedRejectionsAndTimeout")] + [Test, TestCaseSource(nameof(GetExpectedRejectionsAndTimeout))] public void PromiseWait_UncaughtRejectionIsSentToUncaughtRejectionHandler(object expectedRejectionValue, int timeout) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -977,20 +986,29 @@ public void PromiseWait_UncaughtRejectionIsSentToUncaughtRejectionHandler(object }; var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - ++expectedCount; - Assert.IsFalse(promise.TryWait(System.TimeSpan.FromMilliseconds(timeout))); - } + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + Assert.IsFalse(promise.TryWait(System.TimeSpan.FromMilliseconds(timeout))); + } - // Run it again with a freshly preserved promise, because the initial promise will have had its rejection suppressed by the other promises. - var secondPreservedPromise = preservedPromise.Preserve(); - preservedPromise.Forget(); - Assert.IsFalse(secondPreservedPromise.TryWait(System.TimeSpan.FromMilliseconds(timeout))); +#if !PROMISE_DEBUG + // Run it again with a freshly retained promise, because the initial promise will have had its rejection suppressed by the other promises. + using (var secondPromiseRetainer = promiseRetainer.WaitAsync().GetRetainer()) + { + Assert.IsFalse(secondPromiseRetainer.WaitAsync().TryWait(System.TimeSpan.FromMilliseconds(timeout))); + } +#endif - secondPreservedPromise.Forget(); + // Run it again with a freshly preserved promise, because the preserved testabled promise will have had its rejection suppressed by the other promises. +#pragma warning disable CS0618 // Type or member is obsolete + var secondPreservedPromise = promiseRetainer.WaitAsync().Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsFalse(secondPreservedPromise.TryWait(System.TimeSpan.FromMilliseconds(timeout))); + secondPreservedPromise.Forget(); + } deferred.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); } @@ -1000,7 +1018,7 @@ public void PromiseWait_UncaughtRejectionIsSentToUncaughtRejectionHandler(object } } - [Test, TestCaseSource("GetExpectedRejectionsAndTimeout")] + [Test, TestCaseSource(nameof(GetExpectedRejectionsAndTimeout))] public void PromiseWaitForResult_UncaughtRejectionIsSentToUncaughtRejectionHandler(object expectedRejectionValue, int timeout) { var currentRejectionHandler = Promise.Config.UncaughtRejectionHandler; @@ -1022,21 +1040,29 @@ public void PromiseWaitForResult_UncaughtRejectionIsSentToUncaughtRejectionHandl }; var deferred = Promise.NewDeferred(); - var preservedPromise = deferred.Promise.Preserve(); - - int outResult; - foreach (var promise in TestHelper.GetTestablePromises(preservedPromise)) + using (var promiseRetainer = deferred.Promise.GetRetainer()) { - ++expectedCount; - Assert.IsFalse(promise.TryWaitForResult(System.TimeSpan.FromMilliseconds(timeout), out outResult)); - } + foreach (var promise in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedCount; + Assert.IsFalse(promise.TryWaitForResult(System.TimeSpan.FromMilliseconds(timeout), out _)); + } - // Run it again with a freshly preserved promise, because the initial promise will have had its rejection suppressed by the other promises. - var secondPreservedPromise = preservedPromise.Preserve(); - preservedPromise.Forget(); - Assert.IsFalse(secondPreservedPromise.TryWaitForResult(System.TimeSpan.FromMilliseconds(timeout), out outResult)); +#if !PROMISE_DEBUG + // Run it again with a freshly retained promise, because the retained promise will have had its rejection suppressed by the other promises. + using (var secondPromiseRetainer = promiseRetainer.WaitAsync().GetRetainer()) + { + Assert.IsFalse(secondPromiseRetainer.WaitAsync().TryWaitForResult(System.TimeSpan.FromMilliseconds(timeout), out _)); + } +#endif - secondPreservedPromise.Forget(); + // Run it again with a freshly preserved promise, because the preserved testabled promise will have had its rejection suppressed by the other promises. +#pragma warning disable CS0618 // Type or member is obsolete + var secondPreservedPromise = promiseRetainer.WaitAsync().Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete + Assert.IsFalse(secondPreservedPromise.TryWaitForResult(System.TimeSpan.FromMilliseconds(timeout), out _)); + secondPreservedPromise.Forget(); + } deferred.Reject(expectedRejectionValue); Assert.AreEqual(expectedCount, uncaughtCount); } @@ -1046,5 +1072,5 @@ public void PromiseWaitForResult_UncaughtRejectionIsSentToUncaughtRejectionHandl } } #endif // !UNITY_WEBGL - } -} \ No newline at end of file + } + } \ No newline at end of file diff --git a/Package/Tests/CoreTests/APIs/WaitAsyncTests.cs b/Package/Tests/CoreTests/APIs/WaitAsyncTests.cs index 29ba2330..be69b5fe 100644 --- a/Package/Tests/CoreTests/APIs/WaitAsyncTests.cs +++ b/Package/Tests/CoreTests/APIs/WaitAsyncTests.cs @@ -44,40 +44,30 @@ public void Teardown() TestHelper.s_expectedUncaughtRejectValue = null; } - private static IEnumerable GetArgs(CompleteType[] completeTypes) + private static IEnumerable GetArgs(params CompleteType[] completeTypes) { - // Make sure we're testing all wait types on the first await. - SynchronizationType[] firstWaitTypes = new SynchronizationType[] + var waitTypes = new[] { SynchronizationType.Synchronous, - SynchronizationType.Foreground, -#if !UNITY_WEBGL - SynchronizationType.Background, -#endif - SynchronizationType.Explicit - }; - SynchronizationType[] secondWaitTypes = new SynchronizationType[] - { - SynchronizationType.Synchronous, - //SynchronizationType.Foreground, // Ignore foreground on second await to reduce number of tests, testing explicit is effectively the same due to the implementation. + //SynchronizationType.Foreground, // Ignore foreground to reduce number of tests, testing explicit is effectively the same. #if !UNITY_WEBGL SynchronizationType.Background, #endif SynchronizationType.Explicit }; - SynchronizationType[] reportTypes = new SynchronizationType[] + SynchronizationType[] reportTypes = new[] { SynchronizationType.Foreground #if !UNITY_WEBGL , SynchronizationType.Background #endif }; - SynchronizationType[] foregroundOnlyReportType = new SynchronizationType[] { SynchronizationType.Foreground }; - bool[] alreadyCompletes = new bool[] { true, false }; + var foregroundOnlyReportType = new[] { SynchronizationType.Foreground }; + var alreadyCompletes = new[] { true, false }; - CompleteType[] secondCompleteTypes = new CompleteType[] { CompleteType.Resolve }; // Just use a single value to reduce number of tests. + var secondCompleteTypes = new[] { CompleteType.Resolve }; // Just use a single value to reduce number of tests. - ConfigureAwaitCancelType[] configureAwaitCancelTypes = new ConfigureAwaitCancelType[] + var configureAwaitCancelTypes = new[] { ConfigureAwaitCancelType.NoToken, ConfigureAwaitCancelType.WithToken_NoCancel, @@ -90,21 +80,23 @@ private static IEnumerable GetArgs(CompleteType[] completeTypes) foreach (CompleteType firstCompleteType in completeTypes) foreach (CompleteType secondCompleteType in secondCompleteTypes) foreach (bool isFirstComplete in alreadyCompletes) - foreach (bool isSecondComplete in alreadyCompletes) - foreach (SynchronizationType firstWaitType in firstWaitTypes) - foreach (SynchronizationType secondWaitType in secondWaitTypes) + foreach (bool isSecondComplete in new[] { false })// alreadyCompletes) // Second always pending to reduce number of tests. + foreach (SynchronizationType firstWaitType in waitTypes) + foreach (SynchronizationType secondWaitType in waitTypes) foreach (SynchronizationType firstReportType in isFirstComplete ? foregroundOnlyReportType : reportTypes) { - var secondReportTypes = !isSecondComplete - ? reportTypes - : new SynchronizationType[] - { - firstWaitType == SynchronizationType.Synchronous - ? firstReportType - : firstWaitType == (SynchronizationType) SynchronizationOption.Background - ? (SynchronizationType) SynchronizationOption.Background - : SynchronizationType.Foreground - }; + // Only report second on foreground to reduce number of tests. + var secondReportTypes = foregroundOnlyReportType; + //var secondReportTypes = !isSecondComplete + // ? reportTypes + // : new SynchronizationType[] + // { + // firstWaitType == SynchronizationType.Synchronous + // ? firstReportType + // : firstWaitType == (SynchronizationType) SynchronizationOption.Background + // ? (SynchronizationType) SynchronizationOption.Background + // : SynchronizationType.Foreground + // }; foreach (SynchronizationType secondReportType in secondReportTypes) { yield return new TestCaseData(firstCompleteType, secondCompleteType, firstWaitType, secondWaitType, firstReportType, secondReportType, configureAwaitCancelType, isFirstComplete, isSecondComplete); @@ -113,28 +105,14 @@ private static IEnumerable GetArgs(CompleteType[] completeTypes) } private static IEnumerable GetArgs_ResolveReject() - { - return GetArgs(new CompleteType[] - { - CompleteType.Resolve, - CompleteType.Reject - }); - } + => GetArgs(CompleteType.Resolve, CompleteType.Reject); private static IEnumerable GetArgs_ContinueWith() - { - return GetArgs(new CompleteType[] - { - CompleteType.Resolve, - CompleteType.Reject, - CompleteType.Cancel - //, CompleteType.CancelFromToken // Ignore CancelFromToken to reduce number of tests. - }); - } + => GetArgs(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel); - private static IEnumerable GetArgs_CancelFinally(CompleteType[] completeTypes) + private static IEnumerable GetArgs_CancelFinally(params CompleteType[] completeTypes) { - SynchronizationType[] synchronizationTypes = new SynchronizationType[] + var synchronizationTypes = new[] { SynchronizationType.Synchronous, SynchronizationType.Foreground, @@ -143,15 +121,15 @@ private static IEnumerable GetArgs_CancelFinally(CompleteType[] co #endif SynchronizationType.Explicit }; - SynchronizationType[] reportTypes = new SynchronizationType[] + var reportTypes = new[] { SynchronizationType.Foreground #if !UNITY_WEBGL , SynchronizationType.Background #endif }; - SynchronizationType[] foregroundOnlyReportType = new SynchronizationType[] { SynchronizationType.Foreground }; - bool[] alreadyCompletes = new bool[] { true, false }; + var foregroundOnlyReportType = new[] { SynchronizationType.Foreground }; + var alreadyCompletes = new[] { true, false }; foreach (CompleteType firstCompleteType in completeTypes) foreach (bool isComplete in alreadyCompletes) @@ -163,24 +141,10 @@ private static IEnumerable GetArgs_CancelFinally(CompleteType[] co } private static IEnumerable GetArgs_Cancel() - { - return GetArgs_CancelFinally(new CompleteType[] - { - CompleteType.Cancel, - CompleteType.CancelFromToken - }); - } + => GetArgs_CancelFinally(CompleteType.Cancel); private static IEnumerable GetArgs_Finally() - { - return GetArgs_CancelFinally(new CompleteType[] - { - CompleteType.Resolve, - CompleteType.Reject, - CompleteType.Cancel, - CompleteType.CancelFromToken - }); - } + => GetArgs_CancelFinally(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel); private readonly TimeSpan timeout = TimeSpan.FromSeconds(2); // promise @@ -195,7 +159,7 @@ private static IEnumerable GetArgs_Finally() // .WaitAsync(SynchronizationOption) // .Then(() => ...) - [Test, TestCaseSource("GetArgs_ResolveReject")] + [Test, TestCaseSource(nameof(GetArgs_ResolveReject))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ResolveReject_void( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -219,215 +183,200 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ResolveReje : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedFirstInvokes = 0; - int expectedSecondInvokes = 0; - - Action onFirstCallback = () => - { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - }; - Action onSecondCallback = () => - { - // We can't assert the context due to thread race conditions, just make sure the callback is invoked. - Interlocked.Increment(ref secondInvokeCounter); - }; - - Func HookupSecondVoid = promise => - { - ++expectedSecondInvokes; - return promise.ContinueWith(_ => onSecondCallback()); - }; - - Func, Promise> HookupSecondT = promise => + using (var firstPromiseRetainer = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out var tryCompleter1).GetRetainer()) { - ++expectedSecondInvokes; - return promise.ContinueWith(_ => { onSecondCallback(); return 2; }); - }; + using (var secondPromiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out var tryCompleter2).GetRetainer()) + { + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - bool isFirstCancelExpected = configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled - || (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst && (!isFirstAlreadyComplete || firstWaitType != SynchronizationType.Synchronous)); + int expectedFirstInvokes = 0; + int expectedSecondInvokes = 0; - if (firstCompleteType == CompleteType.Resolve) - { - TestHelper.AddResolveCallbacksWithCancelation(firstPromise, - onResolve: () => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onAdoptCallbackAdded: (ref Promise promise) => - { - promise = HookupSecondVoid(promise); - }, - onAdoptCallbackAddedConvert: (ref Promise promise) => - { - promise = HookupSecondT(promise); - }, - onCancel: () => - { - if (isFirstCancelExpected) - { - // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. - Interlocked.Increment(ref firstInvokeCounter); - } - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - } - TestHelper.AddCallbacksWithCancelation(firstPromise, - onResolve: () => onFirstCallback(), - onReject: r => onFirstCallback(), - onUnknownRejection: () => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onDirectCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise = promise.Catch(() => { }); - }, - onDirectCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise = promise.Catch(() => 2); - }, - onDirectCallbackAddedCatch: (ref Promise promise) => - { - if (firstCompleteType != CompleteType.Resolve) - { - ++expectedFirstInvokes; - } - // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. - else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + Action onFirstCallback = () => { - ++expectedFirstInvokes; - } - }, - onAdoptCallbackAdded: (ref Promise promise, AdoptLocation adoptLocation) => - { - ++expectedFirstInvokes; - if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) - { - promise = HookupSecondVoid(promise); - } - }, - onAdoptCallbackAddedConvert: (ref Promise promise, AdoptLocation adoptLocation) => - { - ++expectedFirstInvokes; - if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + }; + Action onSecondCallback = () => { - promise = HookupSecondT(promise); - } - }, - onAdoptCallbackAddedCatch: (ref Promise promise) => - { - if (firstCompleteType != CompleteType.Resolve) + // We can't assert the context due to thread race conditions, just make sure the callback is invoked. + Interlocked.Increment(ref secondInvokeCounter); + }; + + Func HookupSecondVoid = promise => { - ++expectedFirstInvokes; - } - // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. - else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + ++expectedSecondInvokes; + return promise.ContinueWith(_ => onSecondCallback()); + }; + + Func, Promise> HookupSecondT = promise => { - ++expectedFirstInvokes; - } - if (firstCompleteType == CompleteType.Reject) + ++expectedSecondInvokes; + return promise.ContinueWith(_ => { onSecondCallback(); return 2; }); + }; + + bool isFirstCancelExpected = configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled + || (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst && (!isFirstAlreadyComplete || firstWaitType != SynchronizationType.Synchronous)); + + if (firstCompleteType == CompleteType.Resolve) { - promise = HookupSecondVoid(promise); + TestHelper.AddResolveCallbacksWithCancelation(firstPromiseRetainer.WaitAsync(), + onResolve: () => onFirstCallback(), + promiseToPromise: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onCallbackAdded: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onAdoptCallbackAdded: (ref Promise promise) => + { + promise = HookupSecondVoid(promise); + }, + onAdoptCallbackAddedConvert: (ref Promise promise) => + { + promise = HookupSecondT(promise); + }, + onCancel: () => + { + if (isFirstCancelExpected) + { + // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. + Interlocked.Increment(ref firstInvokeCounter); + } + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); } - }, - onCancel: () => - { - if (isFirstCancelExpected) + TestHelper.AddCallbacksWithCancelation(firstPromiseRetainer.WaitAsync(), + onResolve: () => onFirstCallback(), + onReject: r => onFirstCallback(), + onUnknownRejection: () => onFirstCallback(), + promiseToPromise: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onDirectCallbackAdded: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise = promise.Catch(() => { }); + }, + onDirectCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise = promise.Catch(() => 2); + }, + onDirectCallbackAddedCatch: (ref Promise promise) => + { + if (firstCompleteType != CompleteType.Resolve) + { + ++expectedFirstInvokes; + } + // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. + else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + { + ++expectedFirstInvokes; + } + }, + onAdoptCallbackAdded: (ref Promise promise, AdoptLocation adoptLocation) => + { + ++expectedFirstInvokes; + if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + { + promise = HookupSecondVoid(promise); + } + }, + onAdoptCallbackAddedConvert: (ref Promise promise, AdoptLocation adoptLocation) => + { + ++expectedFirstInvokes; + if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + { + promise = HookupSecondT(promise); + } + }, + onAdoptCallbackAddedCatch: (ref Promise promise) => + { + if (firstCompleteType != CompleteType.Resolve) + { + ++expectedFirstInvokes; + } + // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. + else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + { + ++expectedFirstInvokes; + } + if (firstCompleteType == CompleteType.Reject) + { + promise = HookupSecondVoid(promise); + } + }, + onCancel: () => + { + if (isFirstCancelExpected) + { + // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. + Interlocked.Increment(ref firstInvokeCounter); + } + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); + + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); + + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + if (firstWaitType != (SynchronizationType) SynchronizationOption.Background) { - // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. - Interlocked.Increment(ref firstInvokeCounter); + Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); } - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + else { - configureAwaitCancelationSource1.Cancel(); + // We check >= instead of == because of race conditions + Assert.GreaterOrEqual(firstInvokeCounter, expectedFirstInvokes); } - TestHelper.GetTryCompleterVoid(firstCompleteType, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (firstWaitType != (SynchronizationType) SynchronizationOption.Background) - { - Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); - } - else - { - // We check >= instead of == because of race conditions - Assert.GreaterOrEqual(firstInvokeCounter, expectedFirstInvokes); - } - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) - { - configureAwaitCancelationSource2.Cancel(); - } - TestHelper.GetTryCompleterT(secondCompleteType, 1, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); + TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedSecondInvokes, timeout, + $"expectedSecondInvokes: {expectedSecondInvokes}, secondInvokeCounter: {secondInvokeCounter}"); + + // Fix a race condition that causes forget to be called before ConfigureAwait. + TestHelper._backgroundContext.WaitForAllThreadsToComplete(); TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedSecondInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedSecondInvokes: " + expectedSecondInvokes + ", secondInvokeCounter: " + secondInvokeCounter); + } } - - // Fix a race condition that causes forget to be called before ConfigureAwait. - TestHelper._backgroundContext.WaitForAllThreadsToComplete(); - TestHelper.ExecuteForegroundCallbacks(); - - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } - [Test, TestCaseSource("GetArgs_ResolveReject")] + [Test, TestCaseSource(nameof(GetArgs_ResolveReject))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ResolveReject_T( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -451,215 +400,200 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ResolveReje : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedFirstInvokes = 0; - int expectedSecondInvokes = 0; - - Action onFirstCallback = () => + using (var firstPromiseRetainer = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out var tryCompleter1).GetRetainer()) { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - }; - Action onSecondCallback = () => - { - // We can't assert the context due to thread race conditions, just make sure the callback is invoked. - Interlocked.Increment(ref secondInvokeCounter); - }; - - Func HookupSecondVoid = promise => - { - ++expectedSecondInvokes; - return promise.ContinueWith(_ => onSecondCallback()); - }; - - Func, Promise> HookupSecondT = promise => - { - ++expectedSecondInvokes; - return promise.ContinueWith(_ => { onSecondCallback(); return 2; }); - }; + using (var secondPromiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out var tryCompleter2).GetRetainer()) + { + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - bool isFirstCancelExpected = configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled - || (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst && (!isFirstAlreadyComplete || firstWaitType != SynchronizationType.Synchronous)); + int expectedFirstInvokes = 0; + int expectedSecondInvokes = 0; - if (firstCompleteType == CompleteType.Resolve) - { - TestHelper.AddResolveCallbacksWithCancelation(firstPromise, - onResolve: v => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onAdoptCallbackAdded: (ref Promise promise) => - { - promise = HookupSecondVoid(promise); - }, - onAdoptCallbackAddedConvert: (ref Promise promise) => - { - promise = HookupSecondT(promise); - }, - onCancel: () => - { - if (isFirstCancelExpected) - { - // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. - Interlocked.Increment(ref firstInvokeCounter); - } - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - } - TestHelper.AddCallbacksWithCancelation(firstPromise, - onResolve: v => onFirstCallback(), - onReject: r => onFirstCallback(), - onUnknownRejection: () => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onDirectCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise = promise.Catch(() => { }); - }, - onDirectCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise = promise.Catch(() => 2); - }, - onDirectCallbackAddedT: (ref Promise promise) => - { - if (firstCompleteType != CompleteType.Resolve) - { - ++expectedFirstInvokes; - } - // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. - else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + Action onFirstCallback = () => { - ++expectedFirstInvokes; - } - }, - onAdoptCallbackAdded: (ref Promise promise, AdoptLocation adoptLocation) => - { - ++expectedFirstInvokes; - if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) - { - promise = HookupSecondVoid(promise); - } - }, - onAdoptCallbackAddedConvert: (ref Promise promise, AdoptLocation adoptLocation) => - { - ++expectedFirstInvokes; - if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + }; + Action onSecondCallback = () => { - promise = HookupSecondT(promise); - } - }, - onAdoptCallbackAddedT: (ref Promise promise) => - { - if (firstCompleteType != CompleteType.Resolve) + // We can't assert the context due to thread race conditions, just make sure the callback is invoked. + Interlocked.Increment(ref secondInvokeCounter); + }; + + Func HookupSecondVoid = promise => { - ++expectedFirstInvokes; - } - // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. - else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + ++expectedSecondInvokes; + return promise.ContinueWith(_ => onSecondCallback()); + }; + + Func, Promise> HookupSecondT = promise => { - ++expectedFirstInvokes; - } - if (firstCompleteType == CompleteType.Reject) + ++expectedSecondInvokes; + return promise.ContinueWith(_ => { onSecondCallback(); return 2; }); + }; + + bool isFirstCancelExpected = configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled + || (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst && (!isFirstAlreadyComplete || firstWaitType != SynchronizationType.Synchronous)); + + if (firstCompleteType == CompleteType.Resolve) { - promise = HookupSecondT(promise); + TestHelper.AddResolveCallbacksWithCancelation(firstPromiseRetainer.WaitAsync(), + onResolve: v => onFirstCallback(), + promiseToPromise: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onCallbackAdded: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onAdoptCallbackAdded: (ref Promise promise) => + { + promise = HookupSecondVoid(promise); + }, + onAdoptCallbackAddedConvert: (ref Promise promise) => + { + promise = HookupSecondT(promise); + }, + onCancel: () => + { + if (isFirstCancelExpected) + { + // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. + Interlocked.Increment(ref firstInvokeCounter); + } + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); } - }, - onCancel: () => - { - if (isFirstCancelExpected) + TestHelper.AddCallbacksWithCancelation(firstPromiseRetainer.WaitAsync(), + onResolve: v => onFirstCallback(), + onReject: r => onFirstCallback(), + onUnknownRejection: () => onFirstCallback(), + promiseToPromise: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => secondPromiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onDirectCallbackAdded: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise = promise.Catch(() => { }); + }, + onDirectCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise = promise.Catch(() => 2); + }, + onDirectCallbackAddedT: (ref Promise promise) => + { + if (firstCompleteType != CompleteType.Resolve) + { + ++expectedFirstInvokes; + } + // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. + else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + { + ++expectedFirstInvokes; + } + }, + onAdoptCallbackAdded: (ref Promise promise, AdoptLocation adoptLocation) => + { + ++expectedFirstInvokes; + if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + { + promise = HookupSecondVoid(promise); + } + }, + onAdoptCallbackAddedConvert: (ref Promise promise, AdoptLocation adoptLocation) => + { + ++expectedFirstInvokes; + if (adoptLocation == AdoptLocation.Both || (CompleteType) adoptLocation == firstCompleteType) + { + promise = HookupSecondT(promise); + } + }, + onAdoptCallbackAddedT: (ref Promise promise) => + { + if (firstCompleteType != CompleteType.Resolve) + { + ++expectedFirstInvokes; + } + // Don't expect cancelation invoke if it will continue on background, as that introduces a race condition. + else if (isFirstCancelExpected && firstWaitType != TestHelper.backgroundType) + { + ++expectedFirstInvokes; + } + if (firstCompleteType == CompleteType.Reject) + { + promise = HookupSecondT(promise); + } + }, + onCancel: () => + { + if (isFirstCancelExpected) + { + // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. + Interlocked.Increment(ref firstInvokeCounter); + } + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); + + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); + + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + if (firstWaitType != (SynchronizationType) SynchronizationOption.Background) { - // Don't assert the context due to a race condition between the cancelation propagating on another thread before the CatchCancelation is hooked up. - Interlocked.Increment(ref firstInvokeCounter); + Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); } - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + else { - configureAwaitCancelationSource1.Cancel(); + // We check >= instead of == because of race conditions + Assert.GreaterOrEqual(firstInvokeCounter, expectedFirstInvokes); } - TestHelper.GetTryCompleterT(firstCompleteType, 1, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); - - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (firstWaitType != (SynchronizationType) SynchronizationOption.Background) - { - Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); - } - else - { - // We check >= instead of == because of race conditions - Assert.GreaterOrEqual(firstInvokeCounter, expectedFirstInvokes); - } - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) - { - configureAwaitCancelationSource2.Cancel(); - } - TestHelper.GetTryCompleterT(secondCompleteType, 1, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); + TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedSecondInvokes, timeout, + $"expectedSecondInvokes: {expectedSecondInvokes}, secondInvokeCounter: {secondInvokeCounter}"); + + // Fix a race condition that causes forget to be called before ConfigureAwait. + TestHelper._backgroundContext.WaitForAllThreadsToComplete(); TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedSecondInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedSecondInvokes: " + expectedSecondInvokes + ", secondInvokeCounter: " + secondInvokeCounter); + } } - - // Fix a race condition that causes forget to be called before ConfigureAwait. - TestHelper._backgroundContext.WaitForAllThreadsToComplete(); - TestHelper.ExecuteForegroundCallbacks(); - - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } - [Test, TestCaseSource("GetArgs_ContinueWith")] + [Test, TestCaseSource(nameof(GetArgs_ContinueWith))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ContinueWith_void( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -683,107 +617,90 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ContinueWit : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedFirstInvokes = 0; - int expectedSecondInvokes = 0; - - Action onFirstCallback = () => + using (var promiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out var tryCompleter2) + .GetRetainer()) { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - }; - Action onSecondCallback = () => - { - // We can't assert the context due to thread race conditions, just make sure the callback is invoked. - Interlocked.Increment(ref secondInvokeCounter); - }; + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - TestHelper.AddContinueCallbacksWithCancelation(firstPromise, - onContinue: _ => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onAdoptCallbackAdded: (ref Promise promise) => - { - ++expectedSecondInvokes; - promise = promise.ContinueWith(_ => onSecondCallback()); - }, - onAdoptCallbackAddedConvert: (ref Promise promise) => + int expectedFirstInvokes = 0; + int expectedSecondInvokes = 0; + + Action onFirstCallback = () => { - ++expectedSecondInvokes; - promise = promise.ContinueWith(_ => { onSecondCallback(); return 2; }); - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - - threadHelper.ExecuteSynchronousOrOnThread( - () => + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + }; + Action onSecondCallback = () => { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + // We can't assert the context due to thread race conditions, just make sure the callback is invoked. + Interlocked.Increment(ref secondInvokeCounter); + }; + + TestHelper.AddContinueCallbacksWithCancelation(TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out var tryCompleter1), + onContinue: _ => onFirstCallback(), + promiseToPromise: p => promiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => promiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onCallbackAdded: (ref Promise promise) => { - configureAwaitCancelationSource1.Cancel(); - } - TestHelper.GetTryCompleterVoid(firstCompleteType, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); + ++expectedFirstInvokes; + promise.Forget(); + }, + onCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onAdoptCallbackAdded: (ref Promise promise) => + { + ++expectedSecondInvokes; + promise = promise.ContinueWith(_ => onSecondCallback()); + }, + onAdoptCallbackAddedConvert: (ref Promise promise) => + { + ++expectedSecondInvokes; + promise = promise.ContinueWith(_ => { onSecondCallback(); return 2; }); + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); + + threadHelper.ExecuteSynchronousOrOnThread( + () => { - configureAwaitCancelationSource2.Cancel(); - } - TestHelper.GetTryCompleterT(secondCompleteType, 1, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); + TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { - // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. - TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedSecondInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedSecondInvokes: " + expectedSecondInvokes + ", secondInvokeCounter: " + secondInvokeCounter); + // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedSecondInvokes, timeout, + $"expectedSecondInvokes: {expectedSecondInvokes}, secondInvokeCounter: {secondInvokeCounter}"); } - - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } - [Test, TestCaseSource("GetArgs_ContinueWith")] + [Test, TestCaseSource(nameof(GetArgs_ContinueWith))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ContinueWith_T( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -807,107 +724,90 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_ContinueWit : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedFirstInvokes = 0; - int expectedSecondInvokes = 0; - - Action onFirstCallback = () => - { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - }; - Action onSecondCallback = () => + using (var promiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out var tryCompleter2) + .GetRetainer()) { - // We can't assert the context due to thread race conditions, just make sure the callback is invoked. - Interlocked.Increment(ref secondInvokeCounter); - }; + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - TestHelper.AddContinueCallbacksWithCancelation(firstPromise, - onContinue: _ => onFirstCallback(), - promiseToPromise: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - promiseToPromiseConvert: p => secondPromise.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), - onCallbackAdded: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onCallbackAddedConvert: (ref Promise promise) => - { - ++expectedFirstInvokes; - promise.Forget(); - }, - onAdoptCallbackAdded: (ref Promise promise) => - { - ++expectedSecondInvokes; - promise = promise.ContinueWith(_ => onSecondCallback()); - }, - onAdoptCallbackAddedConvert: (ref Promise promise) => + int expectedFirstInvokes = 0; + int expectedSecondInvokes = 0; + + Action onFirstCallback = () => { - ++expectedSecondInvokes; - promise = promise.ContinueWith(_ => { onSecondCallback(); return 2; }); - }, - configureAwaitType: (ConfigureAwaitType) firstWaitType, - waitAsyncCancelationToken: configureAwaitCancelationToken1, - configureAwaitForceAsync: true - ); - - threadHelper.ExecuteSynchronousOrOnThread( - () => + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + }; + Action onSecondCallback = () => { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + // We can't assert the context due to thread race conditions, just make sure the callback is invoked. + Interlocked.Increment(ref secondInvokeCounter); + }; + + TestHelper.AddContinueCallbacksWithCancelation(TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out var tryCompleter1), + onContinue: _ => onFirstCallback(), + promiseToPromise: p => promiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + promiseToPromiseConvert: p => promiseRetainer.WaitAsync().ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2), + onCallbackAdded: (ref Promise promise) => { - configureAwaitCancelationSource1.Cancel(); - } - TestHelper.GetTryCompleterT(firstCompleteType, 1, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); + ++expectedFirstInvokes; + promise.Forget(); + }, + onCallbackAddedConvert: (ref Promise promise) => + { + ++expectedFirstInvokes; + promise.Forget(); + }, + onAdoptCallbackAdded: (ref Promise promise) => + { + ++expectedSecondInvokes; + promise = promise.ContinueWith(_ => onSecondCallback()); + }, + onAdoptCallbackAddedConvert: (ref Promise promise) => + { + ++expectedSecondInvokes; + promise = promise.ContinueWith(_ => { onSecondCallback(); return 2; }); + }, + configureAwaitType: (ConfigureAwaitType) firstWaitType, + waitAsyncCancelationToken: configureAwaitCancelationToken1, + configureAwaitForceAsync: true + ); - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedFirstInvokes, firstInvokeCounter); + + threadHelper.ExecuteSynchronousOrOnThread( + () => { - configureAwaitCancelationSource2.Cancel(); - } - TestHelper.GetTryCompleterT(secondCompleteType, 1, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); + TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { - // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. - TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedSecondInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedSecondInvokes: " + expectedSecondInvokes + ", secondInvokeCounter: " + secondInvokeCounter); + // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise returning in the callback. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedSecondInvokes, timeout, + $"expectedSecondInvokes: {expectedSecondInvokes}, secondInvokeCounter: {secondInvokeCounter}"); } - - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } - [Test, TestCaseSource("GetArgs_Cancel")] + [Test, TestCaseSource(nameof(GetArgs_Cancel))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Cancel_void( CompleteType completeType, SynchronizationType waitType, @@ -917,50 +817,45 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Cancel_void var foregroundThread = Thread.CurrentThread; var threadHelper = new ThreadHelper(); - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .CatchCancelation(() => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .CatchCancelation(1, cv => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; - threadHelper.ExecuteSynchronousOrOnThread( - () => TestHelper.GetTryCompleterVoid(completeType, rejectValue).Invoke(deferred, cancelationSource), - reportType == SynchronizationType.Foreground); + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .CatchCancelation(() => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); + } + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .CatchCancelation(1, cv => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); + } - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, invokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + tryCompleter, + reportType == SynchronizationType.Foreground); - promise.Forget(); - cancelationSource.TryDispose(); + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, invokeCounter); + } } - [Test, TestCaseSource("GetArgs_Cancel")] + [Test, TestCaseSource(nameof(GetArgs_Cancel))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Cancel_T( CompleteType completeType, SynchronizationType waitType, @@ -970,50 +865,45 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Cancel_T( var foregroundThread = Thread.CurrentThread; var threadHelper = new ThreadHelper(); - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .CatchCancelation(() => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .CatchCancelation(1, cv => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; - threadHelper.ExecuteSynchronousOrOnThread( - () => TestHelper.GetTryCompleterT(completeType, 1, rejectValue).Invoke(deferred, cancelationSource), - reportType == SynchronizationType.Foreground); + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .CatchCancelation(() => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); + } + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .CatchCancelation(1, cv => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); + } - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, invokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + tryCompleter, + reportType == SynchronizationType.Foreground); - promise.Forget(); - cancelationSource.TryDispose(); + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, invokeCounter); + } } - [Test, TestCaseSource("GetArgs_Finally")] + [Test, TestCaseSource(nameof(GetArgs_Finally))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Finally_void( CompleteType completeType, SynchronizationType waitType, @@ -1023,52 +913,47 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Finally_voi var foregroundThread = Thread.CurrentThread; var threadHelper = new ThreadHelper(); - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .Finally(() => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Catch(() => { }) - .Forget(); - } - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .Finally(1, cv => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Catch(() => { }) - .Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; - threadHelper.ExecuteSynchronousOrOnThread( - () => TestHelper.GetTryCompleterVoid(completeType, rejectValue).Invoke(deferred, cancelationSource), - reportType == SynchronizationType.Foreground); + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .Finally(() => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Catch(() => { }) + .Forget(); + } + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .Finally(1, cv => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Catch(() => { }) + .Forget(); + } - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, invokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + tryCompleter, + reportType == SynchronizationType.Foreground); - promise.Forget(); - cancelationSource.TryDispose(); + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, invokeCounter); + } } - [Test, TestCaseSource("GetArgs_Finally")] + [Test, TestCaseSource(nameof(GetArgs_Finally))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Finally_T( CompleteType completeType, SynchronizationType waitType, @@ -1078,49 +963,44 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Finally_T( var foregroundThread = Thread.CurrentThread; var threadHelper = new ThreadHelper(); - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .Finally(() => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Catch(() => { }) - .Forget(); - } - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - p.ConfigureAwait((ConfigureAwaitType) waitType) - .Finally(1, cv => - { - TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); - Interlocked.Increment(ref invokeCounter); - }) - .Catch(() => { }) - .Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; - threadHelper.ExecuteSynchronousOrOnThread( - () => TestHelper.GetTryCompleterT(completeType, 1, rejectValue).Invoke(deferred, cancelationSource), - reportType == SynchronizationType.Foreground); + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .Finally(() => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Catch(() => { }) + .Forget(); + } + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) + { + ++expectedInvokes; + p.ConfigureAwait((ConfigureAwaitType) waitType) + .Finally(1, cv => + { + TestHelper.AssertCallbackContext(waitType, reportType, foregroundThread); + Interlocked.Increment(ref invokeCounter); + }) + .Catch(() => { }) + .Forget(); + } - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, invokeCounter); + threadHelper.ExecuteSynchronousOrOnThread( + tryCompleter, + reportType == SynchronizationType.Foreground); - promise.Forget(); - cancelationSource.TryDispose(); + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, invokeCounter); + } } [Test] @@ -1145,9 +1025,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_void( { lock (lockObj) { - CancelationSource cancelationSource; - Promise.Deferred deferred; - TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, rejectValue, out deferred, out cancelationSource) + TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, rejectValue, out var tryCompleter) .ConfigureAwait(waitType, forceAsync) .ContinueWith(_ => { @@ -1158,7 +1036,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_void( } }) .Forget(); - TestHelper.GetTryCompleterVoid(CompleteType.Resolve, rejectValue).Invoke(deferred, cancelationSource); + tryCompleter(); Assert.AreNotEqual(forceAsync, didInvoke); } }; @@ -1174,10 +1052,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_void( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didInvoke, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didInvoke: " + didInvoke); - } + TestHelper.SpinUntil(() => didInvoke, timeout, $"didInvoke: {didInvoke}"); } [Test] @@ -1202,9 +1077,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_T( { lock (lockObj) { - CancelationSource cancelationSource; - Promise.Deferred deferred; - TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, 1, rejectValue, out deferred, out cancelationSource) + TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, 1, rejectValue, out var tryCompleter) .ConfigureAwait(waitType, forceAsync) .ContinueWith(_ => { @@ -1215,7 +1088,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_T( } }) .Forget(); - TestHelper.GetTryCompleterT(CompleteType.Resolve, 1, rejectValue).Invoke(deferred, cancelationSource); + tryCompleter(); Assert.AreNotEqual(forceAsync, didInvoke); } }; @@ -1231,13 +1104,10 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_Then_T( TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didInvoke, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didInvoke: " + didInvoke); - } + TestHelper.SpinUntil(() => didInvoke, timeout, $"didInvoke: {didInvoke}"); } - [Test, TestCaseSource("GetArgs_ContinueWith")] + [Test, TestCaseSource(nameof(GetArgs_ContinueWith))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Await_void( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -1261,108 +1131,94 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Await_void( : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedInvokes = 0; - bool hasRaceCondition = firstWaitType == (SynchronizationType) SynchronizationOption.Background && secondWaitType == SynchronizationType.Synchronous && !isSecondAlreadyComplete; - - foreach (var p1 in TestHelper.GetTestablePromises(firstPromise)) + using (var firstPromiseRetainer = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, rejectValue, out var tryCompleter1).GetRetainer()) { - ++expectedInvokes; - RunAsync(p1, secondPromise).Forget(); - } - foreach (var p2 in TestHelper.GetTestablePromises(secondPromise)) - { - ++expectedInvokes; - RunAsync(firstPromise, p2).Forget(); - } - - async Promise RunAsync(Promise p1, Promise p2) - { - try - { - await p1.ConfigureAwait((ConfigureAwaitType) firstWaitType, true, configureAwaitCancelationToken1); - } - catch { } - finally + using (var secondPromiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, rejectValue, out var tryCompleter2).GetRetainer()) { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - } + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - try - { - await p2.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2); - } - catch { } - finally - { - // If there's a race condition, p2 could be completed on a separate thread before it's awaited, causing the assert to fail. - // This only matters for SynchronizationOption.Synchronous, for which the caller does not care on what context it executes. - if (!hasRaceCondition) + int expectedInvokes = 0; + bool hasRaceCondition = firstWaitType == (SynchronizationType) SynchronizationOption.Background && secondWaitType == SynchronizationType.Synchronous && !isSecondAlreadyComplete; + + foreach (var p1 in TestHelper.GetTestablePromises(firstPromiseRetainer)) { - TestHelper.AssertCallbackContext(secondWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? secondWaitType : secondReportType, foregroundThread); + ++expectedInvokes; + RunAsync(p1, secondPromiseRetainer.WaitAsync()).Forget(); } - Interlocked.Increment(ref secondInvokeCounter); - } - } - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + // We don't include preserved promise for the second promise, because it becomes forgotten before it's awaited in the async function. + foreach (var p2 in TestHelper.GetTestablePromises(secondPromiseRetainer, includePreserved: false)) { - configureAwaitCancelationSource1.Cancel(); + ++expectedInvokes; + RunAsync(firstPromiseRetainer.WaitAsync(), p2).Forget(); } - TestHelper.GetTryCompleterVoid(firstCompleteType, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); - - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, firstInvokeCounter); - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + async Promise RunAsync(Promise p1, Promise p2) { - configureAwaitCancelationSource2.Cancel(); + try + { + await p1.ConfigureAwait((ConfigureAwaitType) firstWaitType, true, configureAwaitCancelationToken1); + } + catch { } + finally + { + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + } + + try + { + await p2.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2); + } + catch { } + finally + { + // If there's a race condition, p2 could be completed on a separate thread before it's awaited, causing the assert to fail. + // This only matters for SynchronizationOption.Synchronous, for which the caller does not care on what context it executes. + if (!hasRaceCondition) + { + TestHelper.AssertCallbackContext(secondWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? secondWaitType : secondReportType, foregroundThread); + } + Interlocked.Increment(ref secondInvokeCounter); + } } - TestHelper.GetTryCompleterVoid(secondCompleteType, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { - // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise being awaited. + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); + + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, firstInvokeCounter); + + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedInvokes: " + expectedInvokes + ", secondInvokeCounter: " + secondInvokeCounter); - } - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); + // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise being awaited. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedInvokes, timeout, + $"expectedInvokes: {expectedInvokes}, secondInvokeCounter: {secondInvokeCounter}"); + } + } configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } - [Test, TestCaseSource("GetArgs_ContinueWith")] + [Test, TestCaseSource(nameof(GetArgs_ContinueWith))] public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Await_T( CompleteType firstCompleteType, CompleteType secondCompleteType, @@ -1386,103 +1242,89 @@ public void CallbacksWillBeInvokedOnTheCorrectSynchronizationContext_Await_T( : configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : configureAwaitCancelationSource2.Token; - CancelationSource firstCancelationSource; - Promise.Deferred firstDeferred; - CancelationSource secondCancelationSource; - Promise.Deferred secondDeferred; - Promise firstPromise = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out firstDeferred, out firstCancelationSource); - Promise secondPromise = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out secondDeferred, out secondCancelationSource); - - firstPromise = firstPromise.Preserve(); - secondPromise = secondPromise.Preserve(); - - int firstInvokeCounter = 0; - int secondInvokeCounter = 0; - - int expectedInvokes = 0; - bool hasRaceCondition = firstWaitType == (SynchronizationType) SynchronizationOption.Background && secondWaitType == SynchronizationType.Synchronous && !isSecondAlreadyComplete; - - foreach (var p1 in TestHelper.GetTestablePromises(firstPromise)) - { - ++expectedInvokes; - RunAsync(p1, secondPromise).Forget(); - } - foreach (var p2 in TestHelper.GetTestablePromises(secondPromise)) - { - ++expectedInvokes; - RunAsync(firstPromise, p2).Forget(); - } - - async Promise RunAsync(Promise p1, Promise p2) + using (var firstPromiseRetainer = TestHelper.BuildPromise(firstCompleteType, isFirstAlreadyComplete, 1, rejectValue, out var tryCompleter1).GetRetainer()) { - try - { - _ = await p1.ConfigureAwait((ConfigureAwaitType) firstWaitType, true, configureAwaitCancelationToken1); - } - catch { } - finally + using (var secondPromiseRetainer = TestHelper.BuildPromise(secondCompleteType, isSecondAlreadyComplete, 1, rejectValue, out var tryCompleter2).GetRetainer()) { - TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); - Interlocked.Increment(ref firstInvokeCounter); - } + int firstInvokeCounter = 0; + int secondInvokeCounter = 0; - try - { - _ = await p2.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2); - } - catch { } - finally - { - // If there's a race condition, p2 could be completed on a separate thread before it's awaited, causing the assert to fail. - // This only matters for SynchronizationOption.Synchronous, for which the caller does not care on what context it executes. - if (!hasRaceCondition) + int expectedInvokes = 0; + bool hasRaceCondition = firstWaitType == (SynchronizationType) SynchronizationOption.Background && secondWaitType == SynchronizationType.Synchronous && !isSecondAlreadyComplete; + + foreach (var p1 in TestHelper.GetTestablePromises(firstPromiseRetainer)) { - TestHelper.AssertCallbackContext(secondWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? secondWaitType : secondReportType, foregroundThread); + ++expectedInvokes; + RunAsync(p1, secondPromiseRetainer.WaitAsync()).Forget(); } - Interlocked.Increment(ref secondInvokeCounter); - } - } - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + // We don't include preserved promise for the second promise, because it becomes forgotten before it's awaited in the async function. + foreach (var p2 in TestHelper.GetTestablePromises(secondPromiseRetainer, includePreserved: false)) { - configureAwaitCancelationSource1.Cancel(); + ++expectedInvokes; + RunAsync(firstPromiseRetainer.WaitAsync(), p2).Forget(); } - TestHelper.GetTryCompleterT(firstCompleteType, 1, rejectValue).Invoke(firstDeferred, firstCancelationSource); - }, - firstReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - Assert.AreEqual(expectedInvokes, firstInvokeCounter); - - threadHelper.ExecuteSynchronousOrOnThread( - () => - { - if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + async Promise RunAsync(Promise p1, Promise p2) { - configureAwaitCancelationSource2.Cancel(); + try + { + _ = await p1.ConfigureAwait((ConfigureAwaitType) firstWaitType, true, configureAwaitCancelationToken1); + } + catch { } + finally + { + TestHelper.AssertCallbackContext(firstWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? firstWaitType : firstReportType, foregroundThread); + Interlocked.Increment(ref firstInvokeCounter); + } + + try + { + _ = await p2.ConfigureAwait((ConfigureAwaitType) secondWaitType, false, configureAwaitCancelationToken2); + } + catch { } + finally + { + // If there's a race condition, p2 could be completed on a separate thread before it's awaited, causing the assert to fail. + // This only matters for SynchronizationOption.Synchronous, for which the caller does not care on what context it executes. + if (!hasRaceCondition) + { + TestHelper.AssertCallbackContext(secondWaitType, configureAwaitCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? secondWaitType : secondReportType, foregroundThread); + } + Interlocked.Increment(ref secondInvokeCounter); + } } - TestHelper.GetTryCompleterT(secondCompleteType, 1, rejectValue).Invoke(secondDeferred, secondCancelationSource); - }, - secondReportType == SynchronizationType.Foreground); - TestHelper.ExecuteForegroundCallbacks(); - if (!SpinWait.SpinUntil(() => - { - // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise being awaited. + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelFirst) + { + configureAwaitCancelationSource1.Cancel(); + } + tryCompleter1(); + }, + firstReportType == SynchronizationType.Foreground); + + TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); + Assert.AreEqual(expectedInvokes, firstInvokeCounter); + + threadHelper.ExecuteSynchronousOrOnThread( + () => + { + if (configureAwaitCancelType == ConfigureAwaitCancelType.CancelSecond) + { + configureAwaitCancelationSource2.Cancel(); + } + tryCompleter2(); + }, + secondReportType == SynchronizationType.Foreground); TestHelper.ExecuteForegroundCallbacks(); - return secondInvokeCounter == expectedInvokes; - }, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", expectedInvokes: " + expectedInvokes + ", secondInvokeCounter: " + secondInvokeCounter); - } - firstPromise.Forget(); - secondPromise.Forget(); - firstCancelationSource.TryDispose(); - secondCancelationSource.TryDispose(); + // We must execute foreground context on every spin to account for the race condition between firstInvokeCounter being incremented and the configured promise being awaited. + TestHelper.SpinUntilWhileExecutingForegroundContext(() => secondInvokeCounter == expectedInvokes, timeout, + $"expectedInvokes: {expectedInvokes}, secondInvokeCounter: {secondInvokeCounter}"); + } + } configureAwaitCancelationSource1.TryDispose(); configureAwaitCancelationSource2.TryDispose(); } @@ -1509,9 +1351,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_await_void( { lock (lockObj) { - CancelationSource cancelationSource; - Promise.Deferred deferred; - var promise = TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, rejectValue, out deferred, out cancelationSource); + var promise = TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, rejectValue, out var tryCompleter); Await().Forget(); @@ -1526,7 +1366,7 @@ async Promise Await() } } - TestHelper.GetTryCompleterVoid(CompleteType.Resolve, rejectValue).Invoke(deferred, cancelationSource); + tryCompleter(); Assert.AreNotEqual(forceAsync, didInvoke); } }; @@ -1542,10 +1382,7 @@ async Promise Await() TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didInvoke, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didInvoke: " + didInvoke); - } + TestHelper.SpinUntil(() => didInvoke, timeout, $"didInvoke: {didInvoke}"); } [Test] @@ -1570,9 +1407,7 @@ public void WaitAsyncForceAsync_CallbacksWillBeInvokedProperly_await_T( { lock (lockObj) { - CancelationSource cancelationSource; - Promise.Deferred deferred; - var promise = TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, 1, rejectValue, out deferred, out cancelationSource); + var promise = TestHelper.BuildPromise(CompleteType.Resolve, isAlreadyComplete, 1, rejectValue, out var tryCompleter); Await().Forget(); @@ -1587,7 +1422,7 @@ async Promise Await() } } - TestHelper.GetTryCompleterT(CompleteType.Resolve, 1, rejectValue).Invoke(deferred, cancelationSource); + tryCompleter(); Assert.AreNotEqual(forceAsync, didInvoke); } }; @@ -1603,15 +1438,12 @@ async Promise Await() TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didInvoke, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didInvoke: " + didInvoke); - } + TestHelper.SpinUntil(() => didInvoke, timeout, $"didInvoke: {didInvoke}"); } [Test] public void WaitAsyncWithCancelationTokenWillBeCompletedProperly_void( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values(ConfigureAwaitCancelType.NoToken, ConfigureAwaitCancelType.WithToken_NoCancel, ConfigureAwaitCancelType.AlreadyCanceled, ConfigureAwaitCancelType.CancelFirst)] ConfigureAwaitCancelType waitAsyncCancelType, [Values] bool isAlreadyComplete) { @@ -1622,72 +1454,67 @@ public void WaitAsyncWithCancelationTokenWillBeCompletedProperly_void( : waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : waitAsyncCancelationSource.Token; - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - bool cancelationExpected = waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled || (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst && !isAlreadyComplete); - var expectedCompleteState = cancelationExpected ? Promise.State.Canceled : (Promise.State) completeType; - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - p.WaitAsync(waitAsyncCancelationToken) - .ContinueWith(container => - { - Assert.AreEqual(expectedCompleteState, container.State); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } - - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - RunAsync(p).Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; + bool cancelationExpected = waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled || (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst && !isAlreadyComplete); + var expectedCompleteState = cancelationExpected ? Promise.State.Canceled : (Promise.State) completeType; - async Promise RunAsync(Promise p) - { - var actualCompleteState = Promise.State.Pending; - try + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) { - await p.WaitAsync(waitAsyncCancelationToken); - actualCompleteState = Promise.State.Resolved; + ++expectedInvokes; + p.WaitAsync(waitAsyncCancelationToken) + .ContinueWith(container => + { + Assert.AreEqual(expectedCompleteState, container.State); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); } - catch (OperationCanceledException) + + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) { - actualCompleteState = Promise.State.Canceled; + ++expectedInvokes; + RunAsync(p).Forget(); } - catch (Exception) + + async Promise RunAsync(Promise p) { - actualCompleteState = Promise.State.Rejected; + var actualCompleteState = Promise.State.Pending; + try + { + await p.WaitAsync(waitAsyncCancelationToken); + actualCompleteState = Promise.State.Resolved; + } + catch (OperationCanceledException) + { + actualCompleteState = Promise.State.Canceled; + } + catch (Exception) + { + actualCompleteState = Promise.State.Rejected; + } + + Assert.AreEqual(expectedCompleteState, actualCompleteState); + Interlocked.Increment(ref invokeCounter); } - Assert.AreEqual(expectedCompleteState, actualCompleteState); - Interlocked.Increment(ref invokeCounter); - } + if (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst) + { + waitAsyncCancelationSource.Cancel(); + } + tryCompleter(); - if (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst) - { - waitAsyncCancelationSource.Cancel(); + Assert.AreEqual(expectedInvokes, invokeCounter); } - TestHelper.GetTryCompleterVoid(completeType, rejectValue).Invoke(deferred, cancelationSource); - - Assert.AreEqual(expectedInvokes, invokeCounter); - - promise.Forget(); - cancelationSource.TryDispose(); waitAsyncCancelationSource.TryDispose(); } [Test] public void WaitAsyncWithCancelationTokenWillBeCompletedProperly_T( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType, + [Values] CompleteType completeType, [Values(ConfigureAwaitCancelType.NoToken, ConfigureAwaitCancelType.WithToken_NoCancel, ConfigureAwaitCancelType.AlreadyCanceled, ConfigureAwaitCancelType.CancelFirst)] ConfigureAwaitCancelType waitAsyncCancelType, [Values] bool isAlreadyComplete) { @@ -1698,66 +1525,61 @@ public void WaitAsyncWithCancelationTokenWillBeCompletedProperly_T( : waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled ? CancelationToken.Canceled() : waitAsyncCancelationSource.Token; - CancelationSource cancelationSource; - Promise.Deferred deferred; - Promise promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out deferred, out cancelationSource); - - promise = promise.Preserve(); - - int invokeCounter = 0; - int expectedInvokes = 0; - bool cancelationExpected = waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled || (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst && !isAlreadyComplete); - var expectedCompleteState = cancelationExpected ? Promise.State.Canceled : (Promise.State) completeType; - - foreach (var p in TestHelper.GetTestablePromises(promise)) + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, 1, rejectValue, out var tryCompleter) + .GetRetainer()) { - ++expectedInvokes; - p.WaitAsync(waitAsyncCancelationToken) - .ContinueWith(container => - { - Assert.AreEqual(expectedCompleteState, container.State); - Interlocked.Increment(ref invokeCounter); - }) - .Forget(); - } - - foreach (var p in TestHelper.GetTestablePromises(promise)) - { - ++expectedInvokes; - RunAsync(p).Forget(); - } + int invokeCounter = 0; + int expectedInvokes = 0; + bool cancelationExpected = waitAsyncCancelType == ConfigureAwaitCancelType.AlreadyCanceled || (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst && !isAlreadyComplete); + var expectedCompleteState = cancelationExpected ? Promise.State.Canceled : (Promise.State) completeType; - async Promise RunAsync(Promise p) - { - var actualCompleteState = Promise.State.Pending; - try + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) { - _ = await p.WaitAsync(waitAsyncCancelationToken); - actualCompleteState = Promise.State.Resolved; + ++expectedInvokes; + p.WaitAsync(waitAsyncCancelationToken) + .ContinueWith(container => + { + Assert.AreEqual(expectedCompleteState, container.State); + Interlocked.Increment(ref invokeCounter); + }) + .Forget(); } - catch (OperationCanceledException) + + foreach (var p in TestHelper.GetTestablePromises(promiseRetainer)) { - actualCompleteState = Promise.State.Canceled; + ++expectedInvokes; + RunAsync(p).Forget(); } - catch (Exception) + + async Promise RunAsync(Promise p) { - actualCompleteState = Promise.State.Rejected; + var actualCompleteState = Promise.State.Pending; + try + { + _ = await p.WaitAsync(waitAsyncCancelationToken); + actualCompleteState = Promise.State.Resolved; + } + catch (OperationCanceledException) + { + actualCompleteState = Promise.State.Canceled; + } + catch (Exception) + { + actualCompleteState = Promise.State.Rejected; + } + + Assert.AreEqual(expectedCompleteState, actualCompleteState); + Interlocked.Increment(ref invokeCounter); } - Assert.AreEqual(expectedCompleteState, actualCompleteState); - Interlocked.Increment(ref invokeCounter); - } + if (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst) + { + waitAsyncCancelationSource.Cancel(); + } + tryCompleter(); - if (waitAsyncCancelType == ConfigureAwaitCancelType.CancelFirst) - { - waitAsyncCancelationSource.Cancel(); + Assert.AreEqual(expectedInvokes, invokeCounter); } - TestHelper.GetTryCompleterT(completeType, 1, rejectValue).Invoke(deferred, cancelationSource); - - Assert.AreEqual(expectedInvokes, invokeCounter); - - promise.Forget(); - cancelationSource.TryDispose(); waitAsyncCancelationSource.TryDispose(); } } diff --git a/Package/Tests/CoreTests/Concurrency/AllConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/AllConcurrencyTests.cs index 55fb3a80..59729d66 100644 --- a/Package/Tests/CoreTests/Concurrency/AllConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/AllConcurrencyTests.cs @@ -47,13 +47,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1) + () => completer0(deferred0), + () => completer1(deferred1) }; var helper = ParallelCombineTestHelper.Create( @@ -67,16 +65,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -97,15 +93,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2) }; var helper = ParallelCombineTestHelper.Create( @@ -119,18 +112,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -154,17 +144,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -178,20 +164,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -213,17 +195,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ Promise.Deferred[] deferreds = null; IEnumerator promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -239,10 +217,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -251,10 +229,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -272,13 +246,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( @@ -293,16 +265,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -323,15 +293,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), }; var helper = ParallelCombineTestHelper.Create( @@ -346,18 +313,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -381,17 +345,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -406,20 +366,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -441,17 +397,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ Promise.Deferred[] deferreds = null; IEnumerator> promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -468,10 +420,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredT(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredT(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredT(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredT(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -480,10 +432,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToAllConcurrently_ () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions diff --git a/Package/Tests/CoreTests/Concurrency/AwaitConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/AwaitConcurrencyTests.cs index 87b413f3..78e4a425 100644 --- a/Package/Tests/CoreTests/Concurrency/AwaitConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/AwaitConcurrencyTests.cs @@ -34,8 +34,8 @@ public void PreservedPromiseMayBeAwaitedConcurrently_PendingThenComplete_void( [Values] CompleteType completeType) { int invokedCount = 0; - var deferred = TestHelper.GetNewDeferredVoid(completeType, out var cancelationSource); - var promise = deferred.Promise.Preserve(); + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel( @@ -47,7 +47,7 @@ async Promise Await() { try { - await promise; + await promiseRetainer; Interlocked.Increment(ref invokedCount); } catch (UnhandledException) @@ -62,22 +62,21 @@ async Promise Await() } ); - promise.Forget(); - TestHelper.GetTryCompleterVoid(completeType, rejectValue).Invoke(deferred, cancelationSource); - cancelationSource.TryDispose(); + promiseRetainer.Dispose(); + TestHelper.GetTryCompleterVoid(completeType, rejectValue).Invoke(deferred); Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } [Test] public void PreservedPromiseMayBeAwaitedConcurrently_AlreadyComplete_void( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType) + [Values] CompleteType completeType) { int invokedCount = 0; - var promise = completeType == CompleteType.Resolve - ? Promise.Resolved().Preserve() + var promiseRetainer = completeType == CompleteType.Resolve + ? Promise.Resolved().GetRetainer() : completeType == CompleteType.Reject - ? Promise.Rejected(rejectValue).Preserve() - : Promise.Canceled().Preserve(); + ? Promise.Rejected(rejectValue).GetRetainer() + : Promise.Canceled().GetRetainer(); var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel( @@ -89,7 +88,7 @@ async Promise Await() { try { - await promise; + await promiseRetainer; Interlocked.Increment(ref invokedCount); } catch (UnhandledException) @@ -104,7 +103,7 @@ async Promise Await() } ); - promise.Forget(); + promiseRetainer.Dispose(); Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } @@ -113,8 +112,8 @@ public void PreservedPromiseMayBeAwaitedConcurrently_PendingThenComplete_T( [Values] CompleteType completeType) { int invokedCount = 0; - var deferred = TestHelper.GetNewDeferredT(completeType, out var cancelationSource); - var promise = deferred.Promise.Preserve(); + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel( @@ -126,7 +125,7 @@ async void Await() { try { - await promise; + await promiseRetainer; Interlocked.Increment(ref invokedCount); } catch (UnhandledException) @@ -141,22 +140,21 @@ async void Await() } ); - promise.Forget(); - TestHelper.GetTryCompleterT(completeType, 1, rejectValue).Invoke(deferred, cancelationSource); - cancelationSource.TryDispose(); + promiseRetainer.Dispose(); + TestHelper.GetTryCompleterT(completeType, 1, rejectValue).Invoke(deferred); Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } [Test] public void PreservedPromiseMayBeAwaitedConcurrently_AlreadyComplete_T( - [Values(CompleteType.Resolve, CompleteType.Reject, CompleteType.Cancel)] CompleteType completeType) + [Values] CompleteType completeType) { int invokedCount = 0; - var promise = completeType == CompleteType.Resolve - ? Promise.Resolved(1).Preserve() + var promiseRetainer = completeType == CompleteType.Resolve + ? Promise.Resolved(1).GetRetainer() : completeType == CompleteType.Reject - ? Promise.Rejected(rejectValue).Preserve() - : Promise.Canceled().Preserve(); + ? Promise.Rejected(rejectValue).GetRetainer() + : Promise.Canceled().GetRetainer(); var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel( @@ -168,7 +166,7 @@ async void Await() { try { - await promise; + await promiseRetainer; Interlocked.Increment(ref invokedCount); } catch (UnhandledException) @@ -183,7 +181,7 @@ async void Await() } ); - promise.Forget(); + promiseRetainer.Dispose(); Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } @@ -203,7 +201,7 @@ public void PromiseMayBeCompletedAndAwaitedConcurrently_void( setup: () => { result = Promise.State.Pending; - deferred = TestHelper.GetNewDeferredVoid(completeType, out cancelationSource); + deferred = Promise.NewDeferred(); promise = deferred.Promise; }, parallelActionsSetup: new Action[] @@ -233,7 +231,7 @@ async Promise Await() }, parallelActions: new Action[] { - () => tryCompleter(deferred, cancelationSource) + () => tryCompleter(deferred) }, teardown: () => { @@ -248,8 +246,7 @@ async Promise Await() case CompleteType.Reject: Assert.AreEqual(Promise.State.Rejected, result); break; - case CompleteType.Cancel: - case CompleteType.CancelFromToken: + default: Assert.AreEqual(Promise.State.Canceled, result); break; } @@ -273,7 +270,7 @@ public void PromiseMayBeCompletedAndAwaitedConcurrently_T( setup: () => { result = Promise.State.Pending; - deferred = TestHelper.GetNewDeferredT(completeType, out cancelationSource); + deferred = Promise.NewDeferred(); promise = deferred.Promise; }, parallelActionsSetup: new Action[] @@ -303,7 +300,7 @@ async void Await() }, parallelActions: new Action[] { - () => tryCompleter(deferred, cancelationSource) + () => tryCompleter(deferred) }, teardown: () => { @@ -318,8 +315,7 @@ async void Await() case CompleteType.Reject: Assert.AreEqual(Promise.State.Rejected, result); break; - case CompleteType.Cancel: - case CompleteType.CancelFromToken: + default: Assert.AreEqual(Promise.State.Canceled, result); break; } diff --git a/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs new file mode 100644 index 00000000..20187aff --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs @@ -0,0 +1,351 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency +{ + public class EachConcurrencyTests + { + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToEachAndConsumedConcurrently_void( + [Values] CombineType combineType, + [Values] CompleteType completeType0, + [Values] CompleteType completeType1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3) + { + const string rejectValue = "Reject"; + + var completer0 = TestHelper.GetCompleterVoid(completeType0, rejectValue); + var completer1 = TestHelper.GetCompleterVoid(completeType1, rejectValue); + var completer2 = TestHelper.GetCompleterVoid(completeType2, rejectValue); + var completer3 = TestHelper.GetCompleterVoid(completeType3, rejectValue); + + var deferred0 = default(Promise.Deferred); + var deferred1 = default(Promise.Deferred); + var deferred2 = default(Promise.Deferred); + var deferred3 = default(Promise.Deferred); + + List parallelActions = new List() + { + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) + }; + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = Promise.Each(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise) + .GetAsyncEnumerator(); + + for (int i = 0; i < 4; ++i) + { + Assert.True(await asyncEnumerator.MoveNextAsync()); + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToEachAndConsumedConcurrently_WithCancelation_void( + [Values] CombineType combineType, + [Values] CompleteType completeType0, + [Values] CompleteType completeType1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3) + { + const string rejectValue = "Reject"; + + var completer0 = TestHelper.GetCompleterVoid(completeType0, rejectValue); + var completer1 = TestHelper.GetCompleterVoid(completeType1, rejectValue); + var completer2 = TestHelper.GetCompleterVoid(completeType2, rejectValue); + var completer3 = TestHelper.GetCompleterVoid(completeType3, rejectValue); + + var deferred0 = default(Promise.Deferred); + var deferred1 = default(Promise.Deferred); + var deferred2 = default(Promise.Deferred); + var deferred3 = default(Promise.Deferred); + var cancelationSource = default(CancelationSource); + + List parallelActions = new List() + { + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3), + () => cancelationSource.Cancel() + }; + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = Promise.Each(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise) + .WithCancelation(cancelationSource.Token) + .GetAsyncEnumerator(); + + try + { + // This is canceled concurrently, so we can't know how many elements we can enumerate before that occurs. + while (await asyncEnumerator.MoveNextAsync()) + { + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + } + finally + { + await asyncEnumerator.DisposeAsync(); + } + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + cancelationSource = CancelationSource.New(); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.Dispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToEachAndConsumedConcurrently_T( + [Values] CombineType combineType, + [Values] CompleteType completeType0, + [Values] CompleteType completeType1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3) + { + const int resolveValue = 42; + const string rejectValue = "Reject"; + + var completer0 = TestHelper.GetCompleterT(completeType0, resolveValue, rejectValue); + var completer1 = TestHelper.GetCompleterT(completeType1, resolveValue, rejectValue); + var completer2 = TestHelper.GetCompleterT(completeType2, resolveValue, rejectValue); + var completer3 = TestHelper.GetCompleterT(completeType3, resolveValue, rejectValue); + + var deferred0 = default(Promise.Deferred); + var deferred1 = default(Promise.Deferred); + var deferred2 = default(Promise.Deferred); + var deferred3 = default(Promise.Deferred); + + List parallelActions = new List() + { + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) + }; + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = Promise.Each(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise) + .GetAsyncEnumerator(); + + for (int i = 0; i < 4; ++i) + { + Assert.True(await asyncEnumerator.MoveNextAsync()); + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Resolved) + { + Assert.AreEqual(resolveValue, result.Value); + } + else if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + Assert.False(await asyncEnumerator.MoveNextAsync()); + await asyncEnumerator.DisposeAsync(); + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToEachAndConsumedConcurrently_WithCancelation_T( + [Values] CombineType combineType, + [Values] CompleteType completeType0, + [Values] CompleteType completeType1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values(CompleteType.Resolve)] CompleteType completeType3) + { + const int resolveValue = 42; + const string rejectValue = "Reject"; + + var completer0 = TestHelper.GetCompleterT(completeType0, resolveValue, rejectValue); + var completer1 = TestHelper.GetCompleterT(completeType1, resolveValue, rejectValue); + var completer2 = TestHelper.GetCompleterT(completeType2, resolveValue, rejectValue); + var completer3 = TestHelper.GetCompleterT(completeType3, resolveValue, rejectValue); + + var deferred0 = default(Promise.Deferred); + var deferred1 = default(Promise.Deferred); + var deferred2 = default(Promise.Deferred); + var deferred3 = default(Promise.Deferred); + var cancelationSource = default(CancelationSource); + + List parallelActions = new List() + { + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3), + () => cancelationSource.Cancel() + }; + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = Promise.Each(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise) + .WithCancelation(cancelationSource.Token) + .GetAsyncEnumerator(); + + try + { + // This is canceled concurrently, so we can't know how many elements we can enumerate before that occurs. + while (await asyncEnumerator.MoveNextAsync()) + { + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Resolved) + { + Assert.AreEqual(resolveValue, result.Value); + } + else if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + } + finally + { + await asyncEnumerator.DisposeAsync(); + } + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + cancelationSource = CancelationSource.New(); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.Dispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs.meta new file mode 100644 index 00000000..5426c6f6 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/EachConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 6d6e57e62c280b540b046bd9a6af3d7a +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/FirstConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/FirstConcurrencyTests.cs index 7e8788c1..11771130 100644 --- a/Package/Tests/CoreTests/Concurrency/FirstConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/FirstConcurrencyTests.cs @@ -47,13 +47,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( @@ -67,16 +65,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -97,15 +93,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), }; var helper = ParallelCombineTestHelper.Create( @@ -119,18 +112,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -154,17 +144,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -178,20 +164,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -213,17 +195,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl Promise.Deferred[] deferreds = null; IEnumerator promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -239,10 +217,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -251,10 +229,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -272,13 +246,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( @@ -293,16 +265,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -323,15 +293,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), }; var helper = ParallelCombineTestHelper.Create( @@ -346,18 +313,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -381,17 +345,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -406,20 +366,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -441,17 +397,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl Promise.Deferred[] deferreds = null; IEnumerator> promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -468,10 +420,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredT(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredT(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredT(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredT(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -480,10 +432,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToFirstConcurrentl () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions diff --git a/Package/Tests/CoreTests/Concurrency/MergeConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/MergeConcurrencyTests.cs index 033d16f2..9ed6bb59 100644 --- a/Package/Tests/CoreTests/Concurrency/MergeConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/MergeConcurrencyTests.cs @@ -46,13 +46,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl var deferred0 = default(Promise.Deferred); var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completerVoid(deferredVoid, cancelationSourceVoid), + () => completer0(deferred0), + () => completerVoid(deferredVoid), }; var helper = ParallelCombineTestHelper.Create( @@ -67,16 +65,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); + deferred0 = Promise.NewDeferred(); + deferredVoid = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSourceVoid.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -94,19 +90,17 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( combineType, () => Promise.Merge(deferred0.Promise, deferred1.Promise), - expectedResolveValue: ValueTuple.Create(1, 2) + expectedResolveValue: (1, 2) ); helper.MaybeAddParallelAction(parallelActions); @@ -115,16 +109,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -145,21 +137,18 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completerVoid(deferredVoid, cancelationSourceVoid), + () => completer0(deferred0), + () => completer1(deferred1), + () => completerVoid(deferredVoid), }; var helper = ParallelCombineTestHelper.Create( combineType, () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2) + expectedResolveValue: (1, 2) ); helper.MaybeAddParallelAction(parallelActions); @@ -168,575 +157,22 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferredVoid = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSourceVoid.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions ); } - [Test] - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T3( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T3void( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values(CompleteType.Resolve)] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values] CompleteType completeTypeVoid) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completerVoid = TestHelper.GetCompleterVoid(completeTypeVoid, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completerVoid(deferredVoid, cancelationSourceVoid), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSourceVoid.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T4( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T4void( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values(CompleteType.Resolve)] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3, - [Values] CompleteType completeTypeVoid) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - var completerVoid = TestHelper.GetCompleterVoid(completeTypeVoid, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completerVoid(deferredVoid, cancelationSourceVoid), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSourceVoid.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T5( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3, - [Values(CompleteType.Resolve)] CompleteType completeType4) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - var completer4 = TestHelper.GetCompleterT(completeType4, 5, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var deferred4 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T5void( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values(CompleteType.Resolve)] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3, - [Values(CompleteType.Resolve)] CompleteType completeType4, - [Values] CompleteType completeTypeVoid) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - var completer4 = TestHelper.GetCompleterT(completeType4, 5, rejectValue); - var completerVoid = TestHelper.GetCompleterVoid(completeTypeVoid, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var deferred4 = default(Promise.Deferred); - var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - () => completerVoid(deferredVoid, cancelationSourceVoid), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - cancelationSourceVoid.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T6( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3, - [Values(CompleteType.Resolve)] CompleteType completeType4, - [Values(CompleteType.Resolve)] CompleteType completeType5) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - var completer4 = TestHelper.GetCompleterT(completeType4, 5, rejectValue); - var completer5 = TestHelper.GetCompleterT(completeType5, 6, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var deferred4 = default(Promise.Deferred); - var deferred5 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - var cancelationSource5 = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - () => completer5(deferred5, cancelationSource5), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise, deferred5.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5, 6) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - deferred5 = TestHelper.GetNewDeferredT(completeType5, out cancelationSource5); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - cancelationSource5.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } - - [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) - public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T6void( - [Values] CombineType combineType, - [Values] CompleteType completeType0, - [Values(CompleteType.Resolve)] CompleteType completeType1, - [Values(CompleteType.Resolve)] CompleteType completeType2, - [Values(CompleteType.Resolve)] CompleteType completeType3, - [Values(CompleteType.Resolve)] CompleteType completeType4, - [Values(CompleteType.Resolve)] CompleteType completeType5, - [Values] CompleteType completeTypeVoid) - { - var completer0 = TestHelper.GetCompleterT(completeType0, 1, rejectValue); - var completer1 = TestHelper.GetCompleterT(completeType1, 2, rejectValue); - var completer2 = TestHelper.GetCompleterT(completeType2, 3, rejectValue); - var completer3 = TestHelper.GetCompleterT(completeType3, 4, rejectValue); - var completer4 = TestHelper.GetCompleterT(completeType4, 5, rejectValue); - var completer5 = TestHelper.GetCompleterT(completeType5, 6, rejectValue); - var completerVoid = TestHelper.GetCompleterVoid(completeTypeVoid, rejectValue); - - var deferred0 = default(Promise.Deferred); - var deferred1 = default(Promise.Deferred); - var deferred2 = default(Promise.Deferred); - var deferred3 = default(Promise.Deferred); - var deferred4 = default(Promise.Deferred); - var deferred5 = default(Promise.Deferred); - var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - var cancelationSource5 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); - - List parallelActions = new List() - { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - () => completer5(deferred5, cancelationSource5), - () => completerVoid(deferredVoid, cancelationSourceVoid), - }; - - var helper = ParallelCombineTestHelper.Create( - combineType, - () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise, deferred5.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5, 6) - ); - helper.MaybeAddParallelAction(parallelActions); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteParallelActionsMaybeWithOffsets( - // setup - () => - { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - deferred5 = TestHelper.GetNewDeferredT(completeType5, out cancelationSource5); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); - helper.Setup(); - }, - // teardown - () => - { - helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - cancelationSource5.TryDispose(); - cancelationSourceVoid.TryDispose(); - Assert.IsTrue(helper.Success); - }, - parallelActions - ); - } + // We don't test T3 - T6 to reduce number of tests. The implementation is basically the same for all of them anyway. [Test] // Only generate up to 2 CompleteTypes (more takes too long to test) public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrently_T7( @@ -764,29 +200,22 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl var deferred4 = default(Promise.Deferred); var deferred5 = default(Promise.Deferred); var deferred6 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - var cancelationSource5 = default(CancelationSource); - var cancelationSource6 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - () => completer5(deferred5, cancelationSource5), - () => completer6(deferred6, cancelationSource6), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3), + () => completer4(deferred4), + () => completer5(deferred5), + () => completer6(deferred6), }; var helper = ParallelCombineTestHelper.Create( combineType, () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise, deferred5.Promise, deferred6.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5, 6, 7) + expectedResolveValue: (1, 2, 3, 4, 5, 6, 7) ); helper.MaybeAddParallelAction(parallelActions); @@ -795,26 +224,19 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - deferred5 = TestHelper.GetNewDeferredT(completeType5, out cancelationSource5); - deferred6 = TestHelper.GetNewDeferredT(completeType6, out cancelationSource6); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + deferred4 = Promise.NewDeferred(); + deferred5 = Promise.NewDeferred(); + deferred6 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - cancelationSource5.TryDispose(); - cancelationSource6.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -850,31 +272,23 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl var deferred5 = default(Promise.Deferred); var deferred6 = default(Promise.Deferred); var deferredVoid = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); - var cancelationSource4 = default(CancelationSource); - var cancelationSource5 = default(CancelationSource); - var cancelationSource6 = default(CancelationSource); - var cancelationSourceVoid = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3), - () => completer4(deferred4, cancelationSource4), - () => completer5(deferred5, cancelationSource5), - () => completer6(deferred6, cancelationSource6), - () => completerVoid(deferredVoid, cancelationSourceVoid), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3), + () => completer4(deferred4), + () => completer5(deferred5), + () => completer6(deferred6), + () => completerVoid(deferredVoid), }; var helper = ParallelCombineTestHelper.Create( combineType, () => Promise.Merge(deferred0.Promise, deferred1.Promise, deferred2.Promise, deferred3.Promise, deferred4.Promise, deferred5.Promise, deferred6.Promise, deferredVoid.Promise), - expectedResolveValue: ValueTuple.Create(1, 2, 3, 4, 5, 6, 7) + expectedResolveValue: (1, 2, 3, 4, 5, 6, 7) ); helper.MaybeAddParallelAction(parallelActions); @@ -883,28 +297,20 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToMergeConcurrentl // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); - deferred4 = TestHelper.GetNewDeferredT(completeType4, out cancelationSource4); - deferred5 = TestHelper.GetNewDeferredT(completeType5, out cancelationSource5); - deferred6 = TestHelper.GetNewDeferredT(completeType6, out cancelationSource6); - deferredVoid = TestHelper.GetNewDeferredVoid(completeTypeVoid, out cancelationSourceVoid); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); + deferred4 = Promise.NewDeferred(); + deferred5 = Promise.NewDeferred(); + deferred6 = Promise.NewDeferred(); + deferredVoid = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); - cancelationSource4.TryDispose(); - cancelationSource5.TryDispose(); - cancelationSource6.TryDispose(); - cancelationSourceVoid.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions diff --git a/Package/Tests/CoreTests/Concurrency/PromiseConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseConcurrencyTests.cs index a6944b04..67438196 100644 --- a/Package/Tests/CoreTests/Concurrency/PromiseConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/PromiseConcurrencyTests.cs @@ -32,11 +32,71 @@ public void Teardown() TestHelper.Cleanup(); } + [Test] + public void PromiseRetainerWithReferenceBacking_DisposeMayOnlyBeCalledOnce_void() + { + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); + + int successCount = 0, invalidCount = 0; + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteMultiActionParallel( + () => + { + try + { + promiseRetainer.Dispose(); + Interlocked.Increment(ref successCount); + } + catch + { + Interlocked.Increment(ref invalidCount); + } + } + ); + + deferred.Resolve(); + Assert.AreEqual(1, successCount); + Assert.AreEqual(ThreadHelper.multiExecutionCount - 1, invalidCount); + } + + [Test] + public void PromiseRetainerWithReferenceBacking_DisposeMayOnlyBeCalledOnce_T() + { + var deferred = Promise.NewDeferred(); + var promiseRetainer = deferred.Promise.GetRetainer(); + + int successCount = 0, invalidCount = 0; + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteMultiActionParallel( + () => + { + try + { + promiseRetainer.Dispose(); + Interlocked.Increment(ref successCount); + } + catch + { + Interlocked.Increment(ref invalidCount); + } + } + ); + + deferred.Resolve(1); + Assert.AreEqual(1, successCount); + Assert.AreEqual(ThreadHelper.multiExecutionCount - 1, invalidCount); + } + [Test] public void PreservedPromiseWithReferenceBacking_ForgetMayOnlyBeCalledOnce_void() { var deferred = Promise.NewDeferred(); +#pragma warning disable CS0618 // Type or member is obsolete var promise = deferred.Promise.Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete int successCount = 0, invalidCount = 0; @@ -65,7 +125,9 @@ public void PreservedPromiseWithReferenceBacking_ForgetMayOnlyBeCalledOnce_void( public void PreservedPromiseWithReferenceBacking_ForgetMayOnlyBeCalledOnce_T() { var deferred = Promise.NewDeferred(); +#pragma warning disable CS0618 // Type or member is obsolete var promise = deferred.Promise.Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete int successCount = 0, invalidCount = 0; @@ -93,6 +155,7 @@ public void PreservedPromiseWithReferenceBacking_ForgetMayOnlyBeCalledOnce_T() [Test] public void PreservedPromiseWithReferenceBacking_DuplicateCalledConcurrentlyAlwaysReturnsUnique_void() { +#pragma warning disable CS0618 // Type or member is obsolete var deferred = Promise.NewDeferred(); var promise = deferred.Promise.Preserve(); @@ -116,11 +179,13 @@ public void PreservedPromiseWithReferenceBacking_DuplicateCalledConcurrentlyAlwa { Assert.Fail("Duplicate returned at least one of the same promise instance. Duplicate should always return a unique instance from a reference-backed promise."); } +#pragma warning restore CS0618 // Type or member is obsolete } [Test] public void PreservedPromiseWithReferenceBacking_DuplicateCalledConcurrentlyAlwaysReturnsUnique_T() { +#pragma warning disable CS0618 // Type or member is obsolete var deferred = Promise.NewDeferred(); var promise = deferred.Promise.Preserve(); @@ -144,11 +209,13 @@ public void PreservedPromiseWithReferenceBacking_DuplicateCalledConcurrentlyAlwa { Assert.Fail("Duplicate returned at least one of the same promise instance. Duplicate should always return a unique instance from a reference-backed promise."); } +#pragma warning restore CS0618 // Type or member is obsolete } [Test] public void PreservedPromiseWithReferenceBacking_PreserveCalledConcurrentlyAlwaysReturnsUnique_void() { +#pragma warning disable CS0618 // Type or member is obsolete var deferred = Promise.NewDeferred(); var promise = deferred.Promise.Preserve(); @@ -172,11 +239,13 @@ public void PreservedPromiseWithReferenceBacking_PreserveCalledConcurrentlyAlway { Assert.Fail("Duplicate returned at least one of the same promise instance. Duplicate should always return a unique instance from a reference-backed promise."); } +#pragma warning restore CS0618 // Type or member is obsolete } [Test] public void PreservedPromiseWithReferenceBacking_PreserveCalledConcurrentlyAlwaysReturnsUnique_T() { +#pragma warning disable CS0618 // Type or member is obsolete var deferred = Promise.NewDeferred(); var promise = deferred.Promise.Preserve(); @@ -200,6 +269,7 @@ public void PreservedPromiseWithReferenceBacking_PreserveCalledConcurrentlyAlway { Assert.Fail("Duplicate returned at least one of the same promise instance. Duplicate should always return a unique instance from a reference-backed promise."); } +#pragma warning restore CS0618 // Type or member is obsolete } [Test] @@ -208,7 +278,6 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_void( { var returnDeferred = default(Promise.Deferred); var returnPromise = default(Promise); - var cancelationSource = default(CancelationSource); Action threadBarrier = null; var tryCompleter = TestHelper.GetTryCompleterVoid(completeType, rejectValue); @@ -226,7 +295,7 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_void( setup: () => { result = Promise.State.Pending; - returnDeferred = TestHelper.GetNewDeferredVoid(completeType, out cancelationSource); + returnDeferred = Promise.NewDeferred(); returnPromise = returnDeferred.Promise; }, parallelActionsSetup: new Action[] @@ -241,12 +310,10 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_void( }, parallelActions: new Action[] { - () => tryCompleter(returnDeferred, cancelationSource) + () => tryCompleter(returnDeferred) }, teardown: () => { - cancelationSource.TryDispose(); - Assert.AreNotEqual(Promise.State.Pending, result); switch (completeType) { @@ -256,8 +323,7 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_void( case CompleteType.Reject: Assert.AreEqual(Promise.State.Rejected, result); break; - case CompleteType.Cancel: - case CompleteType.CancelFromToken: + default: Assert.AreEqual(Promise.State.Canceled, result); break; } @@ -272,7 +338,6 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_T( { var returnDeferred = default(Promise.Deferred); var returnPromise = default(Promise); - var cancelationSource = default(CancelationSource); Action threadBarrier = null; var tryCompleter = TestHelper.GetTryCompleterT(completeType, 1, rejectValue); @@ -290,7 +355,7 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_T( setup: () => { result = Promise.State.Pending; - returnDeferred = TestHelper.GetNewDeferredT(completeType, out cancelationSource); + returnDeferred = Promise.NewDeferred(); returnPromise = returnDeferred.Promise; }, parallelActionsSetup: new Action[] @@ -305,12 +370,10 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_T( }, parallelActions: new Action[] { - () => tryCompleter(returnDeferred, cancelationSource) + () => tryCompleter(returnDeferred) }, teardown: () => { - cancelationSource.TryDispose(); - Assert.AreNotEqual(Promise.State.Pending, result); switch (completeType) { @@ -320,8 +383,7 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_T( case CompleteType.Reject: Assert.AreEqual(Promise.State.Rejected, result); break; - case CompleteType.Cancel: - case CompleteType.CancelFromToken: + default: Assert.AreEqual(Promise.State.Canceled, result); break; } @@ -331,228 +393,102 @@ public void PromiseReturnedInCallbackMayBeCompletedConcurrently_T( } [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_PendingThenResolved_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var resolveActions = TestHelper.ResolveActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActionsVoid(() => Interlocked.Increment(ref invokedCount), null); - var threadHelper = new ThreadHelper(); - foreach (var action in resolveActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Resolve(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.resolveVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_Resolved_void() - { - int invokedCount = 0; - var promise = Promise.Resolved().Preserve(); - - var resolveActions = TestHelper.ResolveActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActionsVoid(() => Interlocked.Increment(ref invokedCount), null); - var threadHelper = new ThreadHelper(); - foreach (var action in resolveActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.resolveVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_PendingThenResolved_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var resolveActions = TestHelper.ResolveActions(v => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActions(v => Interlocked.Increment(ref invokedCount), null); - var threadHelper = new ThreadHelper(); - foreach (var action in resolveActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Resolve(1); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.resolveTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_Resolved_T() - { - int invokedCount = 0; - var promise = Promise.Resolved(1).Preserve(); - - var resolveActions = TestHelper.ResolveActions(v => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActions(v => Interlocked.Increment(ref invokedCount), null); - var threadHelper = new ThreadHelper(); - foreach (var action in resolveActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.resolveTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_PendingThenRejected_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var catchActions = TestHelper.CatchActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActionsVoid(null, () => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in catchActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Reject("Reject"); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.rejectVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_Rejected_void() - { - int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); - - var catchActions = TestHelper.CatchActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActionsVoid(null, () => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in catchActions.Concat(thenActions)) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.rejectVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_PendingThenRejected_T() + public void PreservedPromise_ThenMayBeCalledConcurrently_void( + [Values(CompleteType.Resolve, CompleteType.Reject)] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete - var catchActions = TestHelper.CatchActions(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActions(null, () => Interlocked.Increment(ref invokedCount)); + var actions = TestHelper.ThenActionsVoid(() => Interlocked.Increment(ref invokedCount), () => Interlocked.Increment(ref invokedCount)) + .Concat(completeType == CompleteType.Resolve + ? TestHelper.ResolveActionsVoid(() => Interlocked.Increment(ref invokedCount)) + : TestHelper.CatchActionsVoid(() => Interlocked.Increment(ref invokedCount)) + ); var threadHelper = new ThreadHelper(); - foreach (var action in catchActions.Concat(thenActions)) + int expectedMultiplier = 0; + foreach (var action in actions) { + ++expectedMultiplier; threadHelper.ExecuteMultiActionParallel(() => action(promise)); } promise.Forget(); - deferred.Reject("Reject"); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.rejectTCallbacks / TestHelper.callbacksMultiplier, invokedCount); + tryCompleter(); + Assert.AreEqual(ThreadHelper.multiExecutionCount * expectedMultiplier, invokedCount); } [Test] - public void PreservedPromiseThenMayBeCalledConcurrently_Rejected_T() + public void PreservedPromise_ThenMayBeCalledConcurrently_T( + [Values(CompleteType.Resolve, CompleteType.Reject)] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 42, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete - var catchActions = TestHelper.CatchActions(() => Interlocked.Increment(ref invokedCount)); - var thenActions = TestHelper.ThenActions(null, () => Interlocked.Increment(ref invokedCount)); + var actions = TestHelper.ThenActions(v => Interlocked.Increment(ref invokedCount), () => Interlocked.Increment(ref invokedCount)) + .Concat(completeType == CompleteType.Resolve + ? TestHelper.ResolveActions(v => Interlocked.Increment(ref invokedCount)) + : TestHelper.CatchActions(() => Interlocked.Increment(ref invokedCount)) + ); var threadHelper = new ThreadHelper(); - foreach (var action in catchActions.Concat(thenActions)) + int expectedMultiplier = 0; + foreach (var action in actions) { + ++expectedMultiplier; threadHelper.ExecuteMultiActionParallel(() => action(promise)); } promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.rejectTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PromiseCatchCancelationnMayBeCalledConcurrently_PendingThenCanceled_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - deferred.Cancel(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); + tryCompleter(); + Assert.AreEqual(ThreadHelper.multiExecutionCount * expectedMultiplier, invokedCount); } [Test] - public void PromiseCatchCancelationnMayBeCalledConcurrently_Canceled_void() + public void PreservedPromise_CatchCancelationMayBeCalledConcurrently_void( + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseCatchCancelationnMayBeCalledConcurrently_PendingThenCanceled_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(CompleteType.Cancel, isAlreadyComplete, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(() => Interlocked.Increment(ref invokedCount)).Forget()); threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); promise.Forget(); - deferred.Cancel(); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); } [Test] - public void PromiseCatchCancelationnMayBeCalledConcurrently_Canceled_T() + public void PreservedPromise_CatchCancelationMayBeCalledConcurrently_T( + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(CompleteType.Cancel, isAlreadyComplete, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(() => Interlocked.Increment(ref invokedCount)).Forget()); threadHelper.ExecuteMultiActionParallel(() => promise.CatchCancelation(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); promise.Forget(); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); } [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenResolved_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Resolve(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Resolved_void() + public void PreservedPromise_ContinueWithMayBeCalledConcurrently_void( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Resolved().Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); var threadHelper = new ThreadHelper(); @@ -561,32 +497,19 @@ public void PreservedPromiseContinueWithMayBeCalledConcurrently_Resolved_void() threadHelper.ExecuteMultiActionParallel(() => action(promise)); } promise.Forget(); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); } [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenResolved_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Resolve(1); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Resolved_T() + public void PreservedPromise_ContinueWithMayBeCalledConcurrently_T( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Resolved(1).Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 42, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); var threadHelper = new ThreadHelper(); @@ -595,311 +518,101 @@ public void PreservedPromiseContinueWithMayBeCalledConcurrently_Resolved_T() threadHelper.ExecuteMultiActionParallel(() => action(promise)); } promise.Forget(); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); } [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenRejected_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Reject("Reject"); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Rejected_void() + public void PreservedPromise_FinallyMayBeCalledConcurrently_void( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); - - var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenRejected_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Reject("Reject"); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Rejected_T() - { - int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); - - var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenCanceled_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Cancel(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Canceled_void() - { - int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); - - var continueActions = TestHelper.ContinueWithActionsVoid(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueVoidCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_PendingThenCanceled_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - deferred.Cancel(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PreservedPromiseContinueWithMayBeCalledConcurrently_Canceled_T() - { - int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); - - var continueActions = TestHelper.ContinueWithActions(() => Interlocked.Increment(ref invokedCount)); - var threadHelper = new ThreadHelper(); - foreach (var action in continueActions) - { - threadHelper.ExecuteMultiActionParallel(() => action(promise)); - } - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * TestHelper.continueTCallbacks / TestHelper.callbacksMultiplier, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenResolved_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - deferred.Resolve(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_Resolved_void() - { - int invokedCount = 0; - var promise = Promise.Resolved().Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenResolved_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - deferred.Resolve(1); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_Resolved_T() - { - int invokedCount = 0; - var promise = Promise.Resolved(1).Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenRejected_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); promise.Forget(); - deferred.Reject("Reject"); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); } [Test] - public void PromiseFinallyMayBeCalledConcurrently_Rejected_void() + public void PreservedPromise_FinallyMayBeCalledConcurrently_T( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); +#pragma warning disable CS0618 // Type or member is obsolete + var promise = TestHelper.BuildPromise(completeType, isAlreadyComplete, 42, "Rejected", out var tryCompleter).Preserve(); +#pragma warning restore CS0618 // Type or member is obsolete var threadHelper = new ThreadHelper(); threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); promise.Forget(); + tryCompleter(); Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); } [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenRejected_T() + public void PromiseRetainer_WaitAsyncMayBeCalledConcurrently_void( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { + var expectedRejection = "Rejected"; int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); - promise.Forget(); - deferred.Reject("Reject"); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_Rejected_T() - { - int invokedCount = 0; - var promise = Promise.Rejected("Reject").Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Catch(() => { }).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenCanceled_void() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - deferred.Cancel(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_Canceled_void() - { - int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); - } - - [Test] - public void PromiseFinallyMayBeCalledConcurrently_PendingThenCanceled_T() - { - int invokedCount = 0; - var deferred = Promise.NewDeferred(); - var promise = deferred.Promise.Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - deferred.Cancel(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedRejection, out var tryCompleter) + .GetRetainer()) + { + new ThreadHelper().ExecuteMultiActionParallel(() => + promiseRetainer.WaitAsync() + .ContinueWith(resultContainer => + { + Interlocked.Increment(ref invokedCount); + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedRejection, resultContainer.Reason); + } + }) + .Forget() + ); + tryCompleter(); + } + Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } [Test] - public void PromiseFinallyMayBeCalledConcurrently_Canceled_T() + public void PromiseRetainer_WaitAsyncMayBeCalledConcurrently_T( + [Values] CompleteType completeType, + [Values] bool isAlreadyComplete) { + var expectedRejection = "Rejected"; + var expectedResult = 42; int invokedCount = 0; - var promise = Promise.Canceled().Preserve(); - - var threadHelper = new ThreadHelper(); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(() => Interlocked.Increment(ref invokedCount)).Forget()); - threadHelper.ExecuteMultiActionParallel(() => promise.Finally(1, cv => Interlocked.Increment(ref invokedCount)).Forget()); - promise.Forget(); - Assert.AreEqual(ThreadHelper.multiExecutionCount * 2, invokedCount); + using (var promiseRetainer = TestHelper.BuildPromise(completeType, isAlreadyComplete, expectedResult, expectedRejection, out var tryCompleter) + .GetRetainer()) + { + new ThreadHelper().ExecuteMultiActionParallel(() => + promiseRetainer.WaitAsync() + .ContinueWith(resultContainer => + { + Interlocked.Increment(ref invokedCount); + Assert.AreEqual((Promise.State) completeType, resultContainer.State); + if (completeType == CompleteType.Reject) + { + Assert.AreEqual(expectedRejection, resultContainer.Reason); + } + }) + .Forget() + ); + tryCompleter(); + } + Assert.AreEqual(ThreadHelper.multiExecutionCount, invokedCount); } } } diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups.meta new file mode 100644 index 00000000..5a9e4f7d --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 04bd6e654ec9dad449637e9c46ac806a +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs new file mode 100644 index 00000000..b5d66955 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs @@ -0,0 +1,116 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseAllGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseAllGroup_AndCancelationTriggeredConcurrently_3( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseAllGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync(), + expectedResolveValue: new[] { 1, 2, 3 } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseAllGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseAllGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, 3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..fff8d97d --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 4fa5d4c4c5e1a9743b0b40012c7020c0 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs new file mode 100644 index 00000000..0fd7d7cb --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs @@ -0,0 +1,115 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseAllResultsGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseAllResultsGroup_AndCancelationTriggeredConcurrently_3( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseAllResultsGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseAllResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseAllResultsGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, 3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..9151bd1e --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseAllResultsGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: d777a1901800aad46a6ec605689820fc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs new file mode 100644 index 00000000..843904ec --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs @@ -0,0 +1,274 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseEachGroupConcurrencyTests + { + [Flags] + public enum EachCancelationType + { + None = 0, + Group = 1 << 0, + Iteration = 1 << 1, + Both = Group | Iteration + } + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseEachGroup_AndCancelationTriggeredConcurrently_void( + [Values] EachCancelationType cancelationType, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + [Values(false)] bool alreadyComplete3) + { + const string rejectValue = "Reject"; + + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var groupCancelationSource = default(CancelationSource); + var iterationCancelationSource = default(CancelationSource); + var iterationCancelationToken = default(CancelationToken); + var group = default(PromiseEachGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (cancelationType.HasFlag(EachCancelationType.Group)) + { + parallelActions.Add(() => groupCancelationSource.Cancel()); + } + if (cancelationType.HasFlag(EachCancelationType.Iteration)) + { + parallelActions.Add(() => iterationCancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .GetAsyncEnumerable(suppressUnobservedRejections: true) + .WithCancelation(iterationCancelationToken) + .GetAsyncEnumerator(); + + try + { + // This is canceled concurrently, so we can't know how many elements we can enumerate before that occurs. + while (await asyncEnumerator.MoveNextAsync()) + { + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + } + finally + { + await asyncEnumerator.DisposeAsync(); + } + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + CancelationToken groupCancelationToken; + if (cancelationType.HasFlag(EachCancelationType.Group)) + { + groupCancelationSource = CancelationSource.New(); + group = PromiseEachGroup.New(groupCancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseEachGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, rejectValue, groupCancelationToken, out tryCompleter3); + if (cancelationType.HasFlag(EachCancelationType.Iteration)) + { + iterationCancelationSource = CancelationSource.New(); + iterationCancelationToken = iterationCancelationSource.Token; + } + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + groupCancelationSource.TryDispose(); + iterationCancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseEachGroup_AndCancelationTriggeredConcurrently_T( + [Values] EachCancelationType cancelationType, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + [Values(false)] bool alreadyComplete3) + { + const string rejectValue = "Reject"; + + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var groupCancelationSource = default(CancelationSource); + var iterationCancelationSource = default(CancelationSource); + var iterationCancelationToken = default(CancelationToken); + var group = default(PromiseEachGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (cancelationType.HasFlag(EachCancelationType.Group)) + { + parallelActions.Add(() => groupCancelationSource.Cancel()); + } + if (cancelationType.HasFlag(EachCancelationType.Iteration)) + { + parallelActions.Add(() => iterationCancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + async () => + { + var asyncEnumerator = group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .GetAsyncEnumerable(suppressUnobservedRejections: true) + .WithCancelation(iterationCancelationToken) + .GetAsyncEnumerator(); + + try + { + // This is canceled concurrently, so we can't know how many elements we can enumerate before that occurs. + while (await asyncEnumerator.MoveNextAsync()) + { + var result = asyncEnumerator.Current; + // We can't assert the state, because the promises are completed concurrently, so their completion order is indeterminate. + if (result.State == Promise.State.Rejected) + { + Assert.AreEqual(rejectValue, result.Reason); + } + } + } + finally + { + await asyncEnumerator.DisposeAsync(); + } + } + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + CancelationToken groupCancelationToken; + if (cancelationType.HasFlag(EachCancelationType.Group)) + { + groupCancelationSource = CancelationSource.New(); + group = PromiseEachGroup.New(groupCancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseEachGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, 3, rejectValue, groupCancelationToken, out tryCompleter3); + if (cancelationType.HasFlag(EachCancelationType.Iteration)) + { + iterationCancelationSource = CancelationSource.New(); + iterationCancelationToken = iterationCancelationSource.Token; + } + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + groupCancelationSource.TryDispose(); + iterationCancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..6b4a7d71 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseEachGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8dc66ff2b062e8b4fac303022207980f +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs new file mode 100644 index 00000000..1e8fb1e0 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs @@ -0,0 +1,197 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseMergeGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseMergeGroup_AndCancelationTriggeredConcurrently_T_void( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete2) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseMergeGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .WaitAsync(), + expectedResolveValue: 1 + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseMergeGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, rejectValue, groupCancelationToken, out tryCompleter2); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseMergeGroup_AndCancelationTriggeredConcurrently_void_T_void_T( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + // Only have up to 2 already complete to reduce test count. + [Values(false)] bool alreadyComplete3, + [Values] CompleteType completeType4, + [Values] bool alreadyComplete4) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var tryCompleter4 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var promise4 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseMergeGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (!alreadyComplete4) + { + parallelActions.Add(() => tryCompleter4()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .Add(promise4) + .WaitAsync(), + expectedResolveValue: (1, 2) + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseMergeGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseMergeGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 1, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, rejectValue, groupCancelationToken, out tryCompleter3); + promise4 = TestHelper.BuildPromise(completeType4, alreadyComplete4, 2, rejectValue, groupCancelationToken, out tryCompleter4); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..87650180 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: bb7e6183071e31345a8b2cb9383e1fb6 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs new file mode 100644 index 00000000..1926158e --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs @@ -0,0 +1,196 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseMergeResultsGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseMergeResultsGroup_AndCancelationTriggeredConcurrently_T_void( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values] CompleteType completeType2, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete2) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseMergeResultsGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseMergeResultsGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, rejectValue, groupCancelationToken, out tryCompleter2); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseMergeResultsGroup_AndCancelationTriggeredConcurrently_void_T_void_T( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete2, + [Values(CompleteType.Resolve)] CompleteType completeType3, + // Only have up to 2 already complete to reduce test count. + [Values(false)] bool alreadyComplete3, + [Values] CompleteType completeType4, + [Values] bool alreadyComplete4) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var tryCompleter4 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var promise4 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseMergeResultsGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (!alreadyComplete4) + { + parallelActions.Add(() => tryCompleter4()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .Add(promise4) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseMergeResultsGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseMergeResultsGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 1, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, rejectValue, groupCancelationToken, out tryCompleter3); + promise4 = TestHelper.BuildPromise(completeType4, alreadyComplete4, 2, rejectValue, groupCancelationToken, out tryCompleter4); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..88f634c6 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseMergeResultsGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 8c02b8ead071eea4a80a0f552a25aabc +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs new file mode 100644 index 00000000..468ac9cc --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs @@ -0,0 +1,195 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseRaceGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseRaceGroup_AndCancelationTriggeredConcurrently_3_void( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseRaceGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseRaceGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseRaceGroup_AndCancelationTriggeredConcurrently_3_T( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseRaceGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseRaceGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseRaceGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, 3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..e9093b9f --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 00b4cf42f1516fe438a2365ec9b6d486 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs new file mode 100644 index 00000000..1bce2a17 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs @@ -0,0 +1,195 @@ +#if !UNITY_WEBGL + +#if PROTO_PROMISE_DEBUG_ENABLE || (!PROTO_PROMISE_DEBUG_DISABLE && DEBUG) +#define PROMISE_DEBUG +#else +#undef PROMISE_DEBUG +#endif + +using NUnit.Framework; +using Proto.Promises; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace ProtoPromiseTests.Concurrency.PromiseGroups +{ + public class PromiseRaceWithIndexGroupConcurrencyTests + { + const string rejectValue = "Fail"; + + [SetUp] + public void Setup() + { + TestHelper.Setup(); + } + + [TearDown] + public void Teardown() + { + TestHelper.Cleanup(); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseRaceWithIndexGroup_AndCancelationTriggeredConcurrently_3_void( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseRaceWithIndexGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseRaceWithIndexGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + + [Test] + public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToPromiseRaceWithIndexGroup_AndCancelationTriggeredConcurrently_3_T( + [Values] bool withCancelation, + [Values] CombineType combineType, + [Values] CompleteType completeType1, + [Values] bool alreadyComplete1, + [Values(CompleteType.Resolve)] CompleteType completeType2, + [Values] bool alreadyComplete2, + [Values] CompleteType completeType3, + // We need at least 1 promise to be pending. + [Values(false)] bool alreadyComplete3) + { + var tryCompleter1 = default(Action); + var tryCompleter2 = default(Action); + var tryCompleter3 = default(Action); + var promise1 = default(Promise); + var promise2 = default(Promise); + var promise3 = default(Promise); + var cancelationSource = default(CancelationSource); + var groupCancelationToken = default(CancelationToken); + var group = default(PromiseRaceWithIndexGroup); + + List parallelActions = new List(); + if (!alreadyComplete1) + { + parallelActions.Add(() => tryCompleter1()); + } + if (!alreadyComplete2) + { + parallelActions.Add(() => tryCompleter2()); + } + if (!alreadyComplete3) + { + parallelActions.Add(() => tryCompleter3()); + } + if (withCancelation) + { + parallelActions.Add(() => cancelationSource.Cancel()); + } + + var helper = ParallelCombineTestHelper.Create( + combineType, + () => group + .Add(promise1) + .Add(promise2) + .Add(promise3) + .WaitAsync() + ); + helper.MaybeAddParallelAction(parallelActions); + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteParallelActionsMaybeWithOffsets( + // setup + () => + { + if (withCancelation) + { + cancelationSource = CancelationSource.New(); + group = PromiseRaceWithIndexGroup.New(cancelationSource.Token, out groupCancelationToken); + } + else + { + group = PromiseRaceWithIndexGroup.New(out groupCancelationToken); + } + promise1 = TestHelper.BuildPromise(completeType1, alreadyComplete1, 1, rejectValue, groupCancelationToken, out tryCompleter1); + promise2 = TestHelper.BuildPromise(completeType2, alreadyComplete2, 2, rejectValue, groupCancelationToken, out tryCompleter2); + promise3 = TestHelper.BuildPromise(completeType3, alreadyComplete3, 3, rejectValue, groupCancelationToken, out tryCompleter3); + helper.Setup(); + }, + // teardown + () => + { + helper.Teardown(); + cancelationSource.TryDispose(); + Assert.IsTrue(helper.Success); + }, + parallelActions + ); + } + } +} + +#endif // !UNITY_WEBGL \ No newline at end of file diff --git a/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs.meta b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs.meta new file mode 100644 index 00000000..7cb00298 --- /dev/null +++ b/Package/Tests/CoreTests/Concurrency/PromiseGroups/PromiseRaceWithIndexGroupConcurrencyTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 9787447060cf9de499f1cd6dde4b2f74 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Package/Tests/CoreTests/Concurrency/PromiseNonPreservedConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/PromiseNonPreservedConcurrencyTests.cs index 365c36f9..9aeba4cb 100644 --- a/Package/Tests/CoreTests/Concurrency/PromiseNonPreservedConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/PromiseNonPreservedConcurrencyTests.cs @@ -94,7 +94,9 @@ public void PromiseWithReferenceBacking_DuplicateMayOnlyBeCalledOnce_void() { try { +#pragma warning disable CS0618 // Type or member is obsolete promise.Duplicate().Forget(); +#pragma warning restore CS0618 // Type or member is obsolete Interlocked.Increment(ref successCount); } catch (Proto.Promises.InvalidOperationException) @@ -123,7 +125,9 @@ public void PromiseWithReferenceBacking_DuplicateMayOnlyBeCalledOnce_T() { try { +#pragma warning disable CS0618 // Type or member is obsolete promise.Duplicate().Forget(); +#pragma warning restore CS0618 // Type or member is obsolete Interlocked.Increment(ref successCount); } catch (Proto.Promises.InvalidOperationException) @@ -152,7 +156,9 @@ public void PromiseWithReferenceBacking_PreserveMayOnlyBeCalledOnce_void() { try { +#pragma warning disable CS0618 // Type or member is obsolete promise.Preserve().Forget(); +#pragma warning restore CS0618 // Type or member is obsolete Interlocked.Increment(ref successCount); } catch (Proto.Promises.InvalidOperationException) @@ -181,7 +187,9 @@ public void PromiseWithReferenceBacking_PreserveMayOnlyBeCalledOnce_T() { try { +#pragma warning disable CS0618 // Type or member is obsolete promise.Preserve().Forget(); +#pragma warning restore CS0618 // Type or member is obsolete Interlocked.Increment(ref successCount); } catch (Proto.Promises.InvalidOperationException) @@ -196,6 +204,68 @@ public void PromiseWithReferenceBacking_PreserveMayOnlyBeCalledOnce_T() Assert.AreEqual(ThreadHelper.multiExecutionCount - 1, invalidCount); } + [Test] + public void PromiseWithReferenceBacking_GetRetainerMayOnlyBeCalledOnce_void() + { + var deferred = Promise.NewDeferred(); + var promise = deferred.Promise; + + int successCount = 0, invalidCount = 0; + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteMultiActionParallel( + () => + { + try + { + using (promise.GetRetainer()) + { + Interlocked.Increment(ref successCount); + } + } + catch (Proto.Promises.InvalidOperationException) + { + Interlocked.Increment(ref invalidCount); + } + } + ); + + deferred.Resolve(); + Assert.AreEqual(1, successCount); + Assert.AreEqual(ThreadHelper.multiExecutionCount - 1, invalidCount); + } + + [Test] + public void PromiseWithReferenceBacking_GetRetainerMayOnlyBeCalledOnce_T() + { + var deferred = Promise.NewDeferred(); + var promise = deferred.Promise; + + int successCount = 0, invalidCount = 0; + + var threadHelper = new ThreadHelper(); + threadHelper.ExecuteMultiActionParallel( + () => + { + try + { + using (promise.GetRetainer()) + { + Interlocked.Increment(ref successCount); + } + } + catch (Proto.Promises.InvalidOperationException) + { + Interlocked.Increment(ref invalidCount); + } + } + ); + + deferred.Resolve(1); + Assert.AreEqual(1, successCount); + Assert.AreEqual(ThreadHelper.multiExecutionCount - 1, invalidCount); + } + private static List> GetThenActions_void() { var actions = new List>(TestHelper.ResolveActionsVoid()); @@ -213,7 +283,7 @@ private static IEnumerable GetThenArgs_void() } // It takes a long time to test all then actions, so split it up for each action. - [Test, TestCaseSource("GetThenArgs_void")] + [Test, TestCaseSource(nameof(GetThenArgs_void))] public void PromiseWithReferenceBacking_ThenMayOnlyBeCalledOnce_void(int thenIndex) { var actions = GetThenActions_void(); @@ -261,7 +331,7 @@ private static IEnumerable GetThenArgs_T() } // It takes a long time to test all then actions, so split it up for each action. - [Test, TestCaseSource("GetThenArgs_T")] + [Test, TestCaseSource(nameof(GetThenArgs_T))] public void PromiseWithReferenceBacking_ThenMayOnlyBeCalledOnce_T(int thenIndex) { var actions = GetThenActions_T(); diff --git a/Package/Tests/CoreTests/Concurrency/RaceConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/RaceConcurrencyTests.cs index 1fbfb678..8f3aedda 100644 --- a/Package/Tests/CoreTests/Concurrency/RaceConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/RaceConcurrencyTests.cs @@ -47,13 +47,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( @@ -67,16 +65,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -97,15 +93,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), }; var helper = ParallelCombineTestHelper.Create( @@ -119,18 +112,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -154,17 +144,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -178,20 +164,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -213,17 +195,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently Promise.Deferred[] deferreds = null; IEnumerator promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -239,10 +217,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredVoid(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredVoid(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredVoid(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredVoid(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -251,10 +229,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -272,13 +246,11 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), + () => completer0(deferred0), + () => completer1(deferred1), }; var helper = ParallelCombineTestHelper.Create( @@ -293,16 +265,14 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -323,15 +293,12 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred0 = default(Promise.Deferred); var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), }; var helper = ParallelCombineTestHelper.Create( @@ -346,18 +313,15 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -381,17 +345,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently var deferred1 = default(Promise.Deferred); var deferred2 = default(Promise.Deferred); var deferred3 = default(Promise.Deferred); - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferred0, cancelationSource0), - () => completer1(deferred1, cancelationSource1), - () => completer2(deferred2, cancelationSource2), - () => completer3(deferred3, cancelationSource3) + () => completer0(deferred0), + () => completer1(deferred1), + () => completer2(deferred2), + () => completer3(deferred3) }; var helper = ParallelCombineTestHelper.Create( @@ -406,20 +366,16 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently // setup () => { - deferred0 = TestHelper.GetNewDeferredT(completeType0, out cancelationSource0); - deferred1 = TestHelper.GetNewDeferredT(completeType1, out cancelationSource1); - deferred2 = TestHelper.GetNewDeferredT(completeType2, out cancelationSource2); - deferred3 = TestHelper.GetNewDeferredT(completeType3, out cancelationSource3); + deferred0 = Promise.NewDeferred(); + deferred1 = Promise.NewDeferred(); + deferred2 = Promise.NewDeferred(); + deferred3 = Promise.NewDeferred(); helper.Setup(); }, // teardown () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions @@ -441,17 +397,13 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently Promise.Deferred[] deferreds = null; IEnumerator> promises = null; - var cancelationSource0 = default(CancelationSource); - var cancelationSource1 = default(CancelationSource); - var cancelationSource2 = default(CancelationSource); - var cancelationSource3 = default(CancelationSource); List parallelActions = new List() { - () => completer0(deferreds[0], cancelationSource0), - () => completer1(deferreds[1], cancelationSource1), - () => completer2(deferreds[2], cancelationSource2), - () => completer3(deferreds[3], cancelationSource3) + () => completer0(deferreds[0]), + () => completer1(deferreds[1]), + () => completer2(deferreds[2]), + () => completer3(deferreds[3]) }; var helper = ParallelCombineTestHelper.Create( @@ -468,10 +420,10 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently { deferreds = new Promise.Deferred[] { - TestHelper.GetNewDeferredT(completeType0, out cancelationSource0), - TestHelper.GetNewDeferredT(completeType1, out cancelationSource1), - TestHelper.GetNewDeferredT(completeType2, out cancelationSource2), - TestHelper.GetNewDeferredT(completeType3, out cancelationSource3) + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred(), + Promise.NewDeferred() }; promises = deferreds.Select(d => d.Promise).GetEnumerator(); helper.Setup(); @@ -480,10 +432,6 @@ public void DeferredsMayBeCompletedWhileTheirPromisesArePassedToRaceConcurrently () => { helper.Teardown(); - cancelationSource0.TryDispose(); - cancelationSource1.TryDispose(); - cancelationSource2.TryDispose(); - cancelationSource3.TryDispose(); Assert.IsTrue(helper.Success); }, parallelActions diff --git a/Package/Tests/CoreTests/Concurrency/Threading/AsyncCountdownEventConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/Threading/AsyncCountdownEventConcurrencyTests.cs index 471c6db2..a0dd1df2 100644 --- a/Package/Tests/CoreTests/Concurrency/Threading/AsyncCountdownEventConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/Threading/AsyncCountdownEventConcurrencyTests.cs @@ -66,7 +66,7 @@ private static IEnumerable GetCountsAndSeparate() yield return new TestCaseData(2, false); } - [Test, TestCaseSource("GetCountsAndSeparate")] + [Test, TestCaseSource(nameof(GetCountsAndSeparate))] public void AsyncCountdownEvent_WaitAndSignalCalledConcurrently(int count, bool separateSignals) { int invokedCount = 0; @@ -147,7 +147,7 @@ public void AsyncCountdownEvent_WaitAndCancelCalledConcurrently_AlreadySet() ); } - [Test, TestCaseSource("GetCountsAndSeparate")] + [Test, TestCaseSource(nameof(GetCountsAndSeparate))] public void AsyncCountdownEvent_WaitAndSignalAndCancelCalledConcurrently(int count, bool separateSignals) { int invokedCount = 0; @@ -228,7 +228,7 @@ public void AsyncCountdownEvent_WaitAsyncCalledConcurrently_AlreadySet() ); } - [Test, TestCaseSource("GetCountsAndSeparate")] + [Test, TestCaseSource(nameof(GetCountsAndSeparate))] public void AsyncCountdownEvent_WaitAsyncAndSetCalledConcurrently(int count, bool separateSignals) { int invokedCount = 0; @@ -312,7 +312,7 @@ public void AsyncCountdownEvent_WaitAsyncAndCancelCalledConcurrently_AlreadySet( ); } - [Test, TestCaseSource("GetCountsAndSeparate")] + [Test, TestCaseSource(nameof(GetCountsAndSeparate))] public void AsyncCountdownEvent_WaitAsyncAndSetAndCancelCalledConcurrently(int count, bool separateSignals) { int invokedCount = 0; diff --git a/Package/Tests/CoreTests/Concurrency/Threading/AsyncReaderWriterLockConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/Threading/AsyncReaderWriterLockConcurrencyTests.cs index 302be4a0..23366804 100644 --- a/Package/Tests/CoreTests/Concurrency/Threading/AsyncReaderWriterLockConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/Threading/AsyncReaderWriterLockConcurrencyTests.cs @@ -115,7 +115,7 @@ async Promise InsideUpgradeableLock(AsyncReaderWriterLock.UpgradeableReaderKey k else { AsyncReaderWriterLock.UpgradedWriterKey writerKey = default; - SpinWait.SpinUntil(() => rwl.TryUpgradeToWriterLock(key, out writerKey)); + TestHelper.SpinUntil(() => rwl.TryUpgradeToWriterLock(key, out writerKey), TimeSpan.FromSeconds(1)); using (writerKey) { InsideWriterLock(); @@ -165,7 +165,7 @@ async Promise InsideUpgradeableLock(AsyncReaderWriterLock.UpgradeableReaderKey k Promise.Run(async () => { AsyncReaderWriterLock.ReaderKey key = default; - SpinWait.SpinUntil(() => rwl.TryEnterReaderLock(out key)); + TestHelper.SpinUntil(() => rwl.TryEnterReaderLock(out key), TimeSpan.FromSeconds(1)); using (key) { InsideReaderLock(); @@ -210,7 +210,7 @@ async Promise InsideUpgradeableLock(AsyncReaderWriterLock.UpgradeableReaderKey k Promise.Run(async () => { AsyncReaderWriterLock.WriterKey key = default; - SpinWait.SpinUntil(() => rwl.TryEnterWriterLock(out key)); + TestHelper.SpinUntil(() => rwl.TryEnterWriterLock(out key), TimeSpan.FromSeconds(1)); using (key) { InsideWriterLock(); @@ -259,7 +259,7 @@ async Promise InsideUpgradeableLock(AsyncReaderWriterLock.UpgradeableReaderKey k Promise.Run(async () => { AsyncReaderWriterLock.UpgradeableReaderKey key = default; - SpinWait.SpinUntil(() => rwl.TryEnterUpgradeableReaderLock(out key)); + TestHelper.SpinUntil(() => rwl.TryEnterUpgradeableReaderLock(out key), TimeSpan.FromSeconds(1)); using (key) { await InsideUpgradeableLock(key, upgradeType); diff --git a/Package/Tests/CoreTests/Concurrency/WaitAsyncConcurrencyTests.cs b/Package/Tests/CoreTests/Concurrency/WaitAsyncConcurrencyTests.cs index 4d96aa3a..b93b093b 100644 --- a/Package/Tests/CoreTests/Concurrency/WaitAsyncConcurrencyTests.cs +++ b/Package/Tests/CoreTests/Concurrency/WaitAsyncConcurrencyTests.cs @@ -78,7 +78,7 @@ private static IEnumerable GetContinuePlace(ActionPlace waitAsyncPl private readonly TimeSpan timeout = TimeSpan.FromSeconds(2); - [Test, TestCaseSource("GetArgs")] + [Test, TestCaseSource(nameof(GetArgs))] public void WaitAsyncContinuationWillBeInvokedOnTheCorrectContext_Concurrent_void( ConfigureAwaitType waitType, ActionPlace waitAsyncSubscribePlace, @@ -195,17 +195,14 @@ async Promise Await() } TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didContinue, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didContinue: " + didContinue); - } + TestHelper.SpinUntil(() => didContinue, timeout, $"didContinue: {didContinue}"); cancelationSource.TryDispose(); }, actions: parallelActions.ToArray() ); } - [Test, TestCaseSource("GetArgs")] + [Test, TestCaseSource(nameof(GetArgs))] public void WaitAsyncContinuationWillBeInvokedOnTheCorrectContext_Concurrent_T( ConfigureAwaitType waitType, ActionPlace waitAsyncSubscribePlace, @@ -322,10 +319,7 @@ async Promise Await() } TestHelper.ExecuteForegroundCallbacksAndWaitForThreadsToComplete(); - if (!SpinWait.SpinUntil(() => didContinue, timeout)) - { - Assert.Fail("Timed out after " + timeout + ", didContinue: " + didContinue); - } + TestHelper.SpinUntil(() => didContinue, timeout, $"didContinue: {didContinue}"); cancelationSource.TryDispose(); }, actions: parallelActions.ToArray() diff --git a/Package/Tests/Helpers/BackgroundSynchronizationContext.cs b/Package/Tests/Helpers/BackgroundSynchronizationContext.cs index 0cc55214..367d0180 100644 --- a/Package/Tests/Helpers/BackgroundSynchronizationContext.cs +++ b/Package/Tests/Helpers/BackgroundSynchronizationContext.cs @@ -91,10 +91,7 @@ public void WaitForAllThreadsToComplete() } TimeSpan timeout = TimeSpan.FromSeconds(runningActions); - if (!SpinWait.SpinUntil(() => _runningActionCount == 0, timeout)) - { - throw new TimeoutException("WaitForAllThreadsToComplete timed out after " + timeout + ", _runningActionCount: " + _runningActionCount); - } + TestHelper.SpinUntil(() => _runningActionCount == 0, timeout, $"WaitForAllThreadsToComplete _runningActionCount: {_runningActionCount}"); } public override SynchronizationContext CreateCopy() diff --git a/Package/Tests/Helpers/ProgressHelper.cs b/Package/Tests/Helpers/ProgressHelper.cs index c554ace0..5bf7a0e4 100644 --- a/Package/Tests/Helpers/ProgressHelper.cs +++ b/Package/Tests/Helpers/ProgressHelper.cs @@ -160,11 +160,8 @@ private bool AreEqual(double expected, double actual) { timeout = TimeSpan.FromSeconds(2); } - double current = double.NaN; - if (!SpinWait.SpinUntil(() => { current = _currentProgress; return AreEqual(expectedProgress, current); }, timeout)) - { - throw new TimeoutException("Progress was not invoked with expected progress " + expectedProgress + " after " + timeout + ", _currentProgress: " + _currentProgress + ", current thread is background: " + Thread.CurrentThread.IsBackground); - } + TestHelper.SpinUntil(() => AreEqual(expectedProgress, _currentProgress), timeout, + $"Progress was not invoked with expected progress {expectedProgress}, _currentProgress: {_currentProgress}, current thread is background: {Thread.CurrentThread.IsBackground}"); } public double GetCurrentProgress(bool waitForInvoke, bool executeForeground, TimeSpan timeout = default(TimeSpan)) diff --git a/Package/Tests/Helpers/TestHelper.cs b/Package/Tests/Helpers/TestHelper.cs index 4961c9f7..3bca3de9 100644 --- a/Package/Tests/Helpers/TestHelper.cs +++ b/Package/Tests/Helpers/TestHelper.cs @@ -22,8 +22,7 @@ public enum CompleteType : byte // Explicit numbers for easy comparison with Promise.State Resolve = 1, Reject = 2, - Cancel = 3, - CancelFromToken, + Cancel = 3 } public enum SynchronizationType @@ -54,6 +53,13 @@ public enum AdoptLocation Both } + public enum CancelationType + { + None, + Deferred, + Immediate + } + public delegate void TestAction(ref T value); public delegate void TestAction(ref T1 value1, T2 value2); @@ -156,7 +162,7 @@ public static void Cleanup() #endif } -#if PROMISE_DEBUG +#if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE Internal.AssertAllObjectsReleased(); #endif @@ -226,146 +232,214 @@ public static float Lerp(float a, float b, float t) return a + (b - a) * t; } - public static Action GetCompleterVoid(CompleteType completeType, string rejectValue) + public static Action GetCompleterVoid(CompleteType completeType, TReject rejectValue) { switch (completeType) { case CompleteType.Resolve: - return (deferred, _) => deferred.Resolve(); + return deferred => deferred.Resolve(); case CompleteType.Reject: - return (deferred, _) => deferred.Reject(rejectValue); + return deferred => deferred.Reject(rejectValue); case CompleteType.Cancel: - return (deferred, _) => deferred.Cancel(); - case CompleteType.CancelFromToken: - return (_, cancelationSource) => cancelationSource.Cancel(); + return deferred => deferred.Cancel(); } throw new Exception(); } - public static Action.Deferred, CancelationSource> GetCompleterT(CompleteType completeType, T resolveValue, string rejectValue) + public static Action.Deferred> GetCompleterT(CompleteType completeType, T resolveValue, TReject rejectValue) { switch (completeType) { case CompleteType.Resolve: - return (deferred, _) => deferred.Resolve(resolveValue); + return deferred => deferred.Resolve(resolveValue); case CompleteType.Reject: - return (deferred, _) => deferred.Reject(rejectValue); + return deferred => deferred.Reject(rejectValue); case CompleteType.Cancel: - return (deferred, _) => deferred.Cancel(); - case CompleteType.CancelFromToken: - return (_, cancelationSource) => cancelationSource.Cancel(); + return deferred => deferred.Cancel(); } throw new Exception(); } - public static Action GetTryCompleterVoid(CompleteType completeType, string rejectValue) + public static Action GetTryCompleterVoid(CompleteType completeType, TReject rejectValue) { switch (completeType) { case CompleteType.Resolve: - return (deferred, _) => deferred.TryResolve(); + return deferred => deferred.TryResolve(); case CompleteType.Reject: - return (deferred, _) => deferred.TryReject(rejectValue); + return deferred => deferred.TryReject(rejectValue); case CompleteType.Cancel: - return (deferred, _) => deferred.TryCancel(); - case CompleteType.CancelFromToken: - return (_, cancelationSource) => cancelationSource.TryCancel(); + return deferred => deferred.TryCancel(); } throw new Exception(); } - public static Action.Deferred, CancelationSource> GetTryCompleterT(CompleteType completeType, T resolveValue, string rejectValue) + public static Action.Deferred> GetTryCompleterT(CompleteType completeType, T resolveValue, TReject rejectValue) { switch (completeType) { case CompleteType.Resolve: - return (deferred, _) => deferred.TryResolve(resolveValue); + return deferred => deferred.TryResolve(resolveValue); case CompleteType.Reject: - return (deferred, _) => deferred.TryReject(rejectValue); + return deferred => deferred.TryReject(rejectValue); case CompleteType.Cancel: - return (deferred, _) => deferred.TryCancel(); - case CompleteType.CancelFromToken: - return (_, cancelationSource) => cancelationSource.TryCancel(); + return deferred => deferred.TryCancel(); } throw new Exception(); } - public static Promise.Deferred GetNewDeferredVoid(CompleteType completeType, out CancelationSource cancelationSource) + public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, TReject reason, out Action tryCompleter) { - cancelationSource = default(CancelationSource); + if (isAlreadyComplete) + { + tryCompleter = () => { }; + switch (completeType) + { + case CompleteType.Resolve: + return Promise.Resolved(); + case CompleteType.Reject: + return Promise.Rejected(reason); + case CompleteType.Cancel: + return Promise.Canceled(); + } + throw new Exception(); + } + var deferred = Promise.NewDeferred(); - if (completeType == CompleteType.CancelFromToken) + switch (completeType) { - cancelationSource = CancelationSource.New(); - cancelationSource.Token.Register(deferred); + case CompleteType.Resolve: + tryCompleter = () => deferred.TryResolve(); + break; + case CompleteType.Reject: + tryCompleter = () => deferred.TryReject(reason); + break; + case CompleteType.Cancel: + tryCompleter = () => deferred.TryCancel(); + break; + default: + throw new Exception(); } - return deferred; + return deferred.Promise; } - public static Promise.Deferred GetNewDeferredT(CompleteType completeType, out CancelationSource cancelationSource) + public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, T value, TReject reason, out Action tryCompleter) { - cancelationSource = default(CancelationSource); + if (isAlreadyComplete) + { + tryCompleter = () => { }; + switch (completeType) + { + case CompleteType.Resolve: + return Promise.Resolved(value); + case CompleteType.Reject: + return Promise.Rejected(reason); + case CompleteType.Cancel: + return Promise.Canceled(); + } + throw new Exception(); + } + var deferred = Promise.NewDeferred(); - if (completeType == CompleteType.CancelFromToken) + switch (completeType) { - cancelationSource = CancelationSource.New(); - cancelationSource.Token.Register(deferred); + case CompleteType.Resolve: + tryCompleter = () => deferred.TryResolve(value); + break; + case CompleteType.Reject: + tryCompleter = () => deferred.TryReject(reason); + break; + case CompleteType.Cancel: + tryCompleter = () => deferred.TryCancel(); + break; + default: + throw new Exception(); } - return deferred; + return deferred.Promise; } - public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, TReject reason, out Promise.Deferred deferred, out CancelationSource cancelationSource) + public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, TReject reason, CancelationToken cancelationToken, out Action tryCompleter) { - if (!isAlreadyComplete) + if (cancelationToken.IsCancelationRequested) { - deferred = GetNewDeferredVoid(completeType, out cancelationSource); - return deferred.Promise; + tryCompleter = () => { }; + return Promise.Canceled(); } - deferred = default(Promise.Deferred); - cancelationSource = default(CancelationSource); - switch (completeType) + if (isAlreadyComplete) { - case CompleteType.Resolve: + tryCompleter = () => { }; + switch (completeType) { - return Promise.Resolved(); + case CompleteType.Resolve: + return Promise.Resolved(); + case CompleteType.Reject: + return Promise.Rejected(reason); + case CompleteType.Cancel: + return Promise.Canceled(); } + throw new Exception(); + } + + var deferred = Promise.NewDeferred(); + cancelationToken.TryRegister(() => deferred.TryCancel(), out _); + switch (completeType) + { + case CompleteType.Resolve: + tryCompleter = () => deferred.TryResolve(); + break; case CompleteType.Reject: - { - return Promise.Rejected(reason); - } + tryCompleter = () => deferred.TryReject(reason); + break; + case CompleteType.Cancel: + tryCompleter = () => deferred.TryCancel(); + break; default: - { - return Promise.Canceled(); - } + throw new Exception(); } + return deferred.Promise; } - public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, T value, TReject reason, out Promise.Deferred deferred, out CancelationSource cancelationSource) + public static Promise BuildPromise(CompleteType completeType, bool isAlreadyComplete, T value, TReject reason, CancelationToken cancelationToken, out Action tryCompleter) { - if (!isAlreadyComplete) + if (cancelationToken.IsCancelationRequested) { - deferred = GetNewDeferredT(completeType, out cancelationSource); - return deferred.Promise; + tryCompleter = () => { }; + return Promise.Canceled(); } - deferred = default(Promise.Deferred); - cancelationSource = default(CancelationSource); - switch (completeType) + if (isAlreadyComplete) { - case CompleteType.Resolve: + tryCompleter = () => { }; + switch (completeType) { - return Promise.Resolved(value); + case CompleteType.Resolve: + return Promise.Resolved(value); + case CompleteType.Reject: + return Promise.Rejected(reason); + case CompleteType.Cancel: + return Promise.Canceled(); } + throw new Exception(); + } + + var deferred = Promise.NewDeferred(); + cancelationToken.TryRegister(() => deferred.TryCancel(), out _); + switch (completeType) + { + case CompleteType.Resolve: + tryCompleter = () => deferred.TryResolve(value); + break; case CompleteType.Reject: - { - return Promise.Rejected(reason); - } + tryCompleter = () => deferred.TryReject(reason); + break; + case CompleteType.Cancel: + tryCompleter = () => deferred.TryCancel(); + break; default: - { - return Promise.Canceled(); - } + throw new Exception(); } + return deferred.Promise; } public static Promise ThenDuplicate(this Promise promise, CancelationToken cancelationToken = default(CancelationToken)) @@ -427,15 +501,7 @@ public static void WaitWithTimeoutWhileExecutingForegroundContext(this Promise p bool isPending = true; promise = promise.Finally(() => isPending = false); - if (!SpinWait.SpinUntil(() => - { - ExecuteForegroundCallbacks(); - return !isPending; - }, timeout)) - { - promise.Forget(); - throw new TimeoutException("Promise wait timed out after " + timeout); - } + SpinUntilWhileExecutingForegroundContext(() => !isPending, timeout); promise.Wait(); } @@ -444,16 +510,38 @@ public static T WaitWithTimeoutWhileExecutingForegroundContext(this Promise isPending = false); + SpinUntilWhileExecutingForegroundContext(() => !isPending, timeout); + return promise.WaitForResult(); + } + + public static void SpinUntil(Func condition, TimeSpan timeout, string message = null) + { + if (!SpinWait.SpinUntil(condition, timeout)) + { + var msg = $"SpinUntil timed out after {timeout}"; + if (message != null) + { + msg += $"; {message}"; + } + throw new TimeoutException(msg); + } + } + + public static void SpinUntilWhileExecutingForegroundContext(Func condition, TimeSpan timeout, string message = null) + { if (!SpinWait.SpinUntil(() => { ExecuteForegroundCallbacks(); - return !isPending; + return condition(); }, timeout)) { - promise.Forget(); - throw new TimeoutException("Promise wait timed out after " + timeout); + var msg = $"SpinUntilWhileExecutingForegroundContext timed out after {timeout}"; + if (message != null) + { + msg += $"; {message}"; + } + throw new TimeoutException(msg); } - return promise.WaitForResult(); } public static void AssertCallbackContext(SynchronizationType expectedContext, SynchronizationType invokeContext, Thread foregroundThread) @@ -491,7 +579,7 @@ public static void AssertCallbackContext(SynchronizationType expectedContext, Sy // The distance between 1 and the largest value smaller than 1. public static readonly float progressEpsilon = 1f - 0.99999994f; - public const int callbacksMultiplier = 4; + public const int callbacksMultiplier = 5; public const int resolveVoidCallbacks = 72 * callbacksMultiplier; public const int resolveTCallbacks = 72 * callbacksMultiplier; @@ -540,14 +628,23 @@ public static void AssertCallbackContext(SynchronizationType expectedContext, Sy public const int cancelTCallbacks = (72 + 8 + 8) * callbacksMultiplier; public const int onCancelCallbacks = 4 * callbacksMultiplier; + public const int onCancelPromiseCallbacks = 2 * callbacksMultiplier; - public static IEnumerable GetTestablePromises(Promise preservedPromise) + public static IEnumerable GetTestablePromises(Promise.Retainer promiseRetainer, bool includePreserved = true) { // This helps to test several different kinds of promises to make sure they all work with the same API. - yield return preservedPromise; - yield return preservedPromise.Duplicate(); + if (includePreserved) + { +#pragma warning disable CS0618 // Type or member is obsolete + var preservedPromise = promiseRetainer.WaitAsync().Preserve(); + yield return preservedPromise; + yield return preservedPromise.Duplicate(); + preservedPromise.Forget(); +#pragma warning restore CS0618 // Type or member is obsolete + } + yield return promiseRetainer.WaitAsync(); var deferred = Promise.NewDeferred(); - preservedPromise + promiseRetainer.WaitAsync() .ContinueWith(deferred, (d, result) => { if (result.State == Promise.State.Resolved) @@ -565,16 +662,24 @@ public static IEnumerable GetTestablePromises(Promise preservedPromise) }) .Forget(); yield return deferred.Promise; - yield return Await(preservedPromise); + yield return Await(promiseRetainer.WaitAsync()); } - public static IEnumerable> GetTestablePromises(Promise preservedPromise) + public static IEnumerable> GetTestablePromises(Promise.Retainer promiseRetainer, bool includePreserved = true) { // This helps to test several different kinds of promises to make sure they all work with the same API. - yield return preservedPromise; - yield return preservedPromise.Duplicate(); + if (includePreserved) + { +#pragma warning disable CS0618 // Type or member is obsolete + var preservedPromise = promiseRetainer.WaitAsync().Preserve(); + yield return preservedPromise; + yield return preservedPromise.Duplicate(); + preservedPromise.Forget(); +#pragma warning restore CS0618 // Type or member is obsolete + } + yield return promiseRetainer.WaitAsync(); var deferred = Promise.NewDeferred(); - preservedPromise + promiseRetainer.WaitAsync() .ContinueWith(deferred, (d, result) => { if (result.State == Promise.State.Resolved) @@ -592,7 +697,7 @@ public static IEnumerable> GetTestablePromises(Promise preserve }) .Forget(); yield return deferred.Promise; - yield return Await(preservedPromise); + yield return Await(promiseRetainer.WaitAsync()); } private static async Promise Await(Promise promise) @@ -614,36 +719,36 @@ public static void AddResolveCallbacks(Promise promise, TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddResolveCallbacksWithCancelation( - promise, - onResolve, convertValue, - onResolveCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - default(CancelationToken), default(CancelationToken), - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddResolveCallbacksWithCancelation( - promise, - onResolve, convertValue, - onResolveCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - cancelationSource.Token, cancelationSource.Token, - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddResolveCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, convertValue, + onResolveCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + default(CancelationToken), default(CancelationToken), + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddResolveCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, convertValue, + onResolveCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddResolveCallbacksWithCancelation(Promise promise, @@ -656,109 +761,109 @@ public static void AddResolveCallbacksWithCancelation(Promis TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegates so no need for null check. - onResolve += () => { }; - onResolveCapture += _ => { }; - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) + using (var retainer = promise.GetRetainer()) { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) - { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - if (onAdoptCallbackAddedConvert == null) - { - onAdoptCallbackAddedConvert = (ref Promise p) => { }; - } - onCancel += () => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p4); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p4); - onCallbackAddedConvert(ref p4); - } + // Add empty delegates so no need for null check. + onResolve += () => { }; + onResolveCapture += _ => { }; + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p7); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p7); - onCallbackAdded(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8); - onCallbackAddedConvert(ref p8); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p4); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p4); + onCallbackAddedConvert(ref p4); + } - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p7); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p7); + onCallbackAdded(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8); + onCallbackAddedConvert(ref p8); + } + } } public static void AddResolveCallbacks(Promise promise, @@ -770,11 +875,12 @@ public static void AddResolveCallbacks(Promise promise TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - AddResolveCallbacksWithCancelation( - promise, + AddResolveCallbacksWithCancelation( + retainer.WaitAsync(), onResolve, convertValue, onResolveCapture, captureValue, promiseToPromise, promiseToPromiseConvert, @@ -785,21 +891,20 @@ public static void AddResolveCallbacks(Promise promise configureAwaitType, configureAwaitForceAsync ); - CancelationSource cancelationSource = CancelationSource.New(); - AddResolveCallbacksWithCancelation( - promise, - onResolve, convertValue, - onResolveCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - cancelationSource.Token, cancelationSource.Token, - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + CancelationSource cancelationSource = CancelationSource.New(); + AddResolveCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, convertValue, + onResolveCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddResolveCallbacksWithCancelation(Promise promise, @@ -812,109 +917,109 @@ public static void AddResolveCallbacksWithCancelation(Pro TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegates so no need for null check. - onResolve += _ => { }; - onResolveCapture += _ => { }; - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) + using (var retainer = promise.GetRetainer()) { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) - { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - if (onAdoptCallbackAddedConvert == null) - { - onAdoptCallbackAddedConvert = (ref Promise p) => { }; - } - onCancel += () => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p4); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p4); - onCallbackAddedConvert(ref p4); - } + // Add empty delegates so no need for null check. + onResolve += _ => { }; + onResolveCapture += _ => { }; + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p7); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p7); - onCallbackAdded(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8); - onCallbackAddedConvert(ref p8); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p4); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p4); + onCallbackAddedConvert(ref p4); + } - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p7); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p7); + onCallbackAdded(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8); + onCallbackAddedConvert(ref p8); + } + } } public static void AddCallbacks(Promise promise, @@ -927,38 +1032,38 @@ public static void AddCallbacks(Promise promise, TestAction onAdoptCallbackAdded = null, TestAction, AdoptLocation> onAdoptCallbackAddedConvert = null, TestAction onAdoptCallbackAddedCatch = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddCallbacksWithCancelation( - promise, - onResolve, onReject, onUnknownRejection, convertValue, - onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - default(CancelationToken), default(CancelationToken), - onCancel, - onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedCatch, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedCatch, - configureAwaitType, configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddCallbacksWithCancelation( - promise, - onResolve, onReject, onUnknownRejection, convertValue, - onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - cancelationSource.Token, cancelationSource.Token, - onCancel, - onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedCatch, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedCatch, - configureAwaitType, configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, onReject, onUnknownRejection, convertValue, + onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + default(CancelationToken), default(CancelationToken), + onCancel, + onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedCatch, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedCatch, + configureAwaitType, configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, onReject, onUnknownRejection, convertValue, + onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedCatch, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedCatch, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddCallbacksWithCancelation(Promise promise, @@ -972,730 +1077,730 @@ public static void AddCallbacksWithCancelation(Prom TestAction onAdoptCallbackAdded = null, TestAction, AdoptLocation> onAdoptCallbackAddedConvert = null, TestAction onAdoptCallbackAddedCatch = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegates so no need for null check. - onResolve += () => { }; - onReject += s => { }; - onUnknownRejection += () => { }; - onResolveCapture += _ => { }; - onRejectCapture += _ => { }; - onUnknownRejectionCapture += _ => { }; - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) - { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onDirectCallbackAdded == null) - { - onDirectCallbackAdded = (ref Promise p) => { }; - } - if (onDirectCallbackAddedConvert == null) - { - onDirectCallbackAddedConvert = (ref Promise p) => { }; - } - if (onDirectCallbackAddedCatch == null) - { - onDirectCallbackAddedCatch = (ref Promise p) => { }; - } - if (onAdoptCallbackAdded == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAdded = (ref Promise p, AdoptLocation _) => { }; - } - if (onAdoptCallbackAddedConvert == null) - { - onAdoptCallbackAddedConvert = (ref Promise p, AdoptLocation _) => { }; - } - if (onAdoptCallbackAddedCatch == null) - { - onAdoptCallbackAddedCatch = (ref Promise p) => { }; - } - onCancel += () => { }; - - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p1); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p2); - onCallbackAdded(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, () => { onUnknownRejection(); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3, AdoptLocation.Reject); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p4); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p4, AdoptLocation.Reject); - onCallbackAdded(ref p4); - } + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p5); - onCallbackAddedConvert(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p6); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p7); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p7, AdoptLocation.Reject); - onCallbackAddedConvert(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8, AdoptLocation.Reject); - onCallbackAddedConvert(ref p8); - } + // Add empty delegates so no need for null check. + onResolve += () => { }; + onReject += s => { }; + onUnknownRejection += () => { }; + onResolveCapture += _ => { }; + onRejectCapture += _ => { }; + onUnknownRejectionCapture += _ => { }; + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onDirectCallbackAdded == null) + { + onDirectCallbackAdded = (ref Promise p) => { }; + } + if (onDirectCallbackAddedConvert == null) + { + onDirectCallbackAddedConvert = (ref Promise p) => { }; + } + if (onDirectCallbackAddedCatch == null) + { + onDirectCallbackAddedCatch = (ref Promise p) => { }; + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p, AdoptLocation _) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p, AdoptLocation _) => { }; + } + if (onAdoptCallbackAddedCatch == null) + { + onAdoptCallbackAddedCatch = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p9 = default(Promise); - p9 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p9); }, () => { onUnknownRejection(); return promiseToPromise(p9); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p9, AdoptLocation.Both); - onCallbackAdded(ref p9); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p10 = default(Promise); - p10 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p10); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p10); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p10, AdoptLocation.Both); - onCallbackAdded(ref p10); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p11 = default(Promise); - p11 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p11); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p11, AdoptLocation.Resolve); - onCallbackAdded(ref p11); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p12 = default(Promise); - p12 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p12); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p12, AdoptLocation.Resolve); - onCallbackAdded(ref p12); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p1); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p2); + onCallbackAdded(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, () => { onUnknownRejection(); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3, AdoptLocation.Reject); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p4); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p4, AdoptLocation.Reject); + onCallbackAdded(ref p4); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p13 = default(Promise); - p13 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p13); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p13); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p13, AdoptLocation.Both); - onCallbackAddedConvert(ref p13); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p14 = default(Promise); - p14 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p14); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p14); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p14, AdoptLocation.Both); - onCallbackAddedConvert(ref p14); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p15 = default(Promise); - p15 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p15); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p15, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p15); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p16 = default(Promise); - p16 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p16); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p16, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p16); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p5); + onCallbackAddedConvert(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p6); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p7); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p7, AdoptLocation.Reject); + onCallbackAddedConvert(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8, AdoptLocation.Reject); + onCallbackAddedConvert(ref p8); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p9 = default(Promise); + p9 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p9); }, () => { onUnknownRejection(); return promiseToPromise(p9); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p9, AdoptLocation.Both); + onCallbackAdded(ref p9); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p10 = default(Promise); + p10 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p10); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p10); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p10, AdoptLocation.Both); + onCallbackAdded(ref p10); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p11 = default(Promise); + p11 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p11); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p11, AdoptLocation.Resolve); + onCallbackAdded(ref p11); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p12 = default(Promise); + p12 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p12); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p12, AdoptLocation.Resolve); + onCallbackAdded(ref p12); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p17 = default(Promise); - p17 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(() => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAddedCatch(ref p17); - onCallbackAdded(ref p17); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p18 = default(Promise); - p18 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch((TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAddedCatch(ref p18); - onCallbackAdded(ref p18); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p19 = default(Promise); - p19 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(() => { onUnknownRejection(); return promiseToPromise(p19); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAddedCatch(ref p19); - onCallbackAdded(ref p19); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p20 = default(Promise); - p20 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch((TReject failValue) => { onReject(failValue); return promiseToPromise(p20); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAddedCatch(ref p20); - onCallbackAdded(ref p20); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p13 = default(Promise); + p13 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p13); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p13); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p13, AdoptLocation.Both); + onCallbackAddedConvert(ref p13); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p14 = default(Promise); + p14 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p14); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p14); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p14, AdoptLocation.Both); + onCallbackAddedConvert(ref p14); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p15 = default(Promise); + p15 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p15); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p15, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p15); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p16 = default(Promise); + p16 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p16); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p16, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p16); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p21 = default(Promise); - p21 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p21); - onCallbackAdded(ref p21); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p22 = default(Promise); - p22 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p22); - onCallbackAdded(ref p22); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p23 = default(Promise); - p23 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p23); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p23, AdoptLocation.Reject); - onCallbackAdded(ref p23); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p24 = default(Promise); - p24 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p24); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p24, AdoptLocation.Reject); - onCallbackAdded(ref p24); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p17 = default(Promise); + p17 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(() => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAddedCatch(ref p17); + onCallbackAdded(ref p17); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p18 = default(Promise); + p18 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch((TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAddedCatch(ref p18); + onCallbackAdded(ref p18); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p19 = default(Promise); + p19 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(() => { onUnknownRejection(); return promiseToPromise(p19); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAddedCatch(ref p19); + onCallbackAdded(ref p19); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p20 = default(Promise); + p20 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch((TReject failValue) => { onReject(failValue); return promiseToPromise(p20); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAddedCatch(ref p20); + onCallbackAdded(ref p20); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p25 = default(Promise); - p25 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p25); - onCallbackAddedConvert(ref p25); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p26 = default(Promise); - p26 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p26); - onCallbackAddedConvert(ref p26); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p27 = default(Promise); - p27 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p27); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p27, AdoptLocation.Reject); - onCallbackAddedConvert(ref p27); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p28 = default(Promise); - p28 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p28); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p28, AdoptLocation.Reject); - onCallbackAddedConvert(ref p28); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p29 = default(Promise); - p29 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p29); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p29); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p29, AdoptLocation.Both); - onCallbackAdded(ref p29); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p30 = default(Promise); - p30 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p30); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p30); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p30, AdoptLocation.Both); - onCallbackAdded(ref p30); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p31 = default(Promise); - p31 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p31); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p31, AdoptLocation.Resolve); - onCallbackAdded(ref p31); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p32 = default(Promise); - p32 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p32); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p32, AdoptLocation.Resolve); - onCallbackAdded(ref p32); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p21 = default(Promise); + p21 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p21); + onCallbackAdded(ref p21); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p22 = default(Promise); + p22 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p22); + onCallbackAdded(ref p22); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p23 = default(Promise); + p23 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p23); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p23, AdoptLocation.Reject); + onCallbackAdded(ref p23); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p24 = default(Promise); + p24 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p24); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p24, AdoptLocation.Reject); + onCallbackAdded(ref p24); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p33 = default(Promise); - p33 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p33); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p33); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p33, AdoptLocation.Both); - onCallbackAddedConvert(ref p33); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p34 = default(Promise); - p34 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p34); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p34); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p34, AdoptLocation.Both); - onCallbackAddedConvert(ref p34); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p35 = default(Promise); - p35 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p35); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p35, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p35); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p36 = default(Promise); - p36 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p36); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p36, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p36); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p25 = default(Promise); + p25 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p25); + onCallbackAddedConvert(ref p25); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p26 = default(Promise); + p26 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p26); + onCallbackAddedConvert(ref p26); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p27 = default(Promise); + p27 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p27); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p27, AdoptLocation.Reject); + onCallbackAddedConvert(ref p27); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p28 = default(Promise); + p28 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p28); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p28, AdoptLocation.Reject); + onCallbackAddedConvert(ref p28); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p29 = default(Promise); + p29 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p29); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p29); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p29, AdoptLocation.Both); + onCallbackAdded(ref p29); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p30 = default(Promise); + p30 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p30); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p30); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p30, AdoptLocation.Both); + onCallbackAdded(ref p30); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p31 = default(Promise); + p31 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p31); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p31, AdoptLocation.Resolve); + onCallbackAdded(ref p31); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p32 = default(Promise); + p32 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p32); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p32, AdoptLocation.Resolve); + onCallbackAdded(ref p32); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p37 = default(Promise); - p37 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAddedCatch(ref p37); - onCallbackAdded(ref p37); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p38 = default(Promise); - p38 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAddedCatch(ref p38); - onCallbackAdded(ref p38); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p39 = default(Promise); - p39 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p39); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAddedCatch(ref p39); - onCallbackAdded(ref p39); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p40 = default(Promise); - p40 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p40); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAddedCatch(ref p40); - onCallbackAdded(ref p40); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p33 = default(Promise); + p33 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p33); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p33); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p33, AdoptLocation.Both); + onCallbackAddedConvert(ref p33); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p34 = default(Promise); + p34 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p34); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p34); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p34, AdoptLocation.Both); + onCallbackAddedConvert(ref p34); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p35 = default(Promise); + p35 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p35); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p35, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p35); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p36 = default(Promise); + p36 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p36); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p36, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p36); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p41 = default(Promise); - p41 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p41); - onCallbackAdded(ref p41); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p42 = default(Promise); - p42 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p42); - onCallbackAdded(ref p42); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p43 = default(Promise); - p43 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, () => { onUnknownRejection(); return promiseToPromise(p43); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p43, AdoptLocation.Reject); - onCallbackAdded(ref p43); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p44 = default(Promise); - p44 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p44); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p44, AdoptLocation.Reject); - onCallbackAdded(ref p44); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p37 = default(Promise); + p37 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAddedCatch(ref p37); + onCallbackAdded(ref p37); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p38 = default(Promise); + p38 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAddedCatch(ref p38); + onCallbackAdded(ref p38); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p39 = default(Promise); + p39 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p39); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAddedCatch(ref p39); + onCallbackAdded(ref p39); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p40 = default(Promise); + p40 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p40); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAddedCatch(ref p40); + onCallbackAdded(ref p40); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p45 = default(Promise); - p45 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p45); - onCallbackAddedConvert(ref p45); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p46 = default(Promise); - p46 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p46); - onCallbackAddedConvert(ref p46); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p47 = default(Promise); - p47 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p47); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p47, AdoptLocation.Reject); - onCallbackAddedConvert(ref p47); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p48 = default(Promise); - p48 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p48); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p48, AdoptLocation.Reject); - onCallbackAddedConvert(ref p48); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p49 = default(Promise); - p49 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p49); }, () => { onUnknownRejection(); return promiseToPromise(p49); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p49, AdoptLocation.Both); - onCallbackAdded(ref p49); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p50 = default(Promise); - p50 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p50); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p50); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p50, AdoptLocation.Both); - onCallbackAdded(ref p50); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p51 = default(Promise); - p51 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p51); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p51, AdoptLocation.Resolve); - onCallbackAdded(ref p51); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p52 = default(Promise); - p52 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p52); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p52, AdoptLocation.Resolve); - onCallbackAdded(ref p52); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p41 = default(Promise); + p41 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p41); + onCallbackAdded(ref p41); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p42 = default(Promise); + p42 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p42); + onCallbackAdded(ref p42); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p43 = default(Promise); + p43 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, () => { onUnknownRejection(); return promiseToPromise(p43); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p43, AdoptLocation.Reject); + onCallbackAdded(ref p43); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p44 = default(Promise); + p44 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p44); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p44, AdoptLocation.Reject); + onCallbackAdded(ref p44); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p53 = default(Promise); - p53 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p53); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p53); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p53, AdoptLocation.Both); - onCallbackAddedConvert(ref p53); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p54 = default(Promise); - p54 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p54); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p54); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p54, AdoptLocation.Both); - onCallbackAddedConvert(ref p54); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p55 = default(Promise); - p55 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p55); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p55, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p55); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p56 = default(Promise); - p56 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p56); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p56, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p56); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p45 = default(Promise); + p45 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p45); + onCallbackAddedConvert(ref p45); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p46 = default(Promise); + p46 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p46); + onCallbackAddedConvert(ref p46); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p47 = default(Promise); + p47 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p47); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p47, AdoptLocation.Reject); + onCallbackAddedConvert(ref p47); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p48 = default(Promise); + p48 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p48); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p48, AdoptLocation.Reject); + onCallbackAddedConvert(ref p48); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p49 = default(Promise); + p49 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p49); }, () => { onUnknownRejection(); return promiseToPromise(p49); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p49, AdoptLocation.Both); + onCallbackAdded(ref p49); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p50 = default(Promise); + p50 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p50); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p50); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p50, AdoptLocation.Both); + onCallbackAdded(ref p50); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p51 = default(Promise); + p51 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p51); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p51, AdoptLocation.Resolve); + onCallbackAdded(ref p51); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p52 = default(Promise); + p52 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromise(p52); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p52, AdoptLocation.Resolve); + onCallbackAdded(ref p52); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p57 = default(Promise); - p57 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p57); - onCallbackAdded(ref p57); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p58 = default(Promise); - p58 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p58); - onCallbackAdded(ref p58); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p59 = default(Promise); - p59 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p59); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p59, AdoptLocation.Reject); - onCallbackAdded(ref p59); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p60 = default(Promise); - p60 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p60); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p60, AdoptLocation.Reject); - onCallbackAdded(ref p60); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p53 = default(Promise); + p53 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p53); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p53); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p53, AdoptLocation.Both); + onCallbackAddedConvert(ref p53); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p54 = default(Promise); + p54 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p54); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p54); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p54, AdoptLocation.Both); + onCallbackAddedConvert(ref p54); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p55 = default(Promise); + p55 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p55); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p55, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p55); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p56 = default(Promise); + p56 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, cv => { onResolveCapture(cv); onResolve(); return promiseToPromiseConvert(p56); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p56, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p56); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p61 = default(Promise); - p61 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p61); - onCallbackAddedConvert(ref p61); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p62 = default(Promise); - p62 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p62); - onCallbackAddedConvert(ref p62); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p63 = default(Promise); - p63 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p63); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p63, AdoptLocation.Reject); - onCallbackAddedConvert(ref p63); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p64 = default(Promise); - p64 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p64); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p64, AdoptLocation.Reject); - onCallbackAddedConvert(ref p64); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p65 = default(Promise); - p65 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p65); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p65); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p65, AdoptLocation.Both); - onCallbackAdded(ref p65); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p66 = default(Promise); - p66 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p66); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p66); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p66, AdoptLocation.Both); - onCallbackAdded(ref p66); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p67 = default(Promise); - p67 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p67); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p67, AdoptLocation.Resolve); - onCallbackAdded(ref p67); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p68 = default(Promise); - p68 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromise(p68); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p68, AdoptLocation.Resolve); - onCallbackAdded(ref p68); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p57 = default(Promise); + p57 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p57); + onCallbackAdded(ref p57); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p58 = default(Promise); + p58 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p58); + onCallbackAdded(ref p58); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p59 = default(Promise); + p59 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p59); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p59, AdoptLocation.Reject); + onCallbackAdded(ref p59); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p60 = default(Promise); + p60 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p60); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p60, AdoptLocation.Reject); + onCallbackAdded(ref p60); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p69 = default(Promise); - p69 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p69); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p69); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p69, AdoptLocation.Both); - onCallbackAddedConvert(ref p69); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p70 = default(Promise); - p70 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p70); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p70); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p70, AdoptLocation.Both); - onCallbackAddedConvert(ref p70); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p71 = default(Promise); - p71 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p71); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p71, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p71); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p72 = default(Promise); - p72 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(() => { onResolve(); return promiseToPromiseConvert(p72); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p72, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p72); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p61 = default(Promise); + p61 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p61); + onCallbackAddedConvert(ref p61); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p62 = default(Promise); + p62 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p62); + onCallbackAddedConvert(ref p62); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p63 = default(Promise); + p63 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p63); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p63, AdoptLocation.Reject); + onCallbackAddedConvert(ref p63); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p64 = default(Promise); + p64 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p64); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p64, AdoptLocation.Reject); + onCallbackAddedConvert(ref p64); + } + + foreach (var p in GetTestablePromises(retainer)) + { + Promise p65 = default(Promise); + p65 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p65); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p65); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p65, AdoptLocation.Both); + onCallbackAdded(ref p65); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p66 = default(Promise); + p66 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p66); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p66); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p66, AdoptLocation.Both); + onCallbackAdded(ref p66); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p67 = default(Promise); + p67 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p67); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p67, AdoptLocation.Resolve); + onCallbackAdded(ref p67); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p68 = default(Promise); + p68 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromise(p68); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p68, AdoptLocation.Resolve); + onCallbackAdded(ref p68); + } - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p69 = default(Promise); + p69 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p69); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p69); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p69, AdoptLocation.Both); + onCallbackAddedConvert(ref p69); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p70 = default(Promise); + p70 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p70); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p70); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p70, AdoptLocation.Both); + onCallbackAddedConvert(ref p70); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p71 = default(Promise); + p71 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p71); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p71, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p71); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p72 = default(Promise); + p72 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(() => { onResolve(); return promiseToPromiseConvert(p72); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p72, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p72); + } + } } public static void AddCallbacks(Promise promise, @@ -1708,39 +1813,39 @@ public static void AddCallbacks(Promise promi TestAction onAdoptCallbackAdded = null, TestAction, AdoptLocation> onAdoptCallbackAddedConvert = null, TestAction> onAdoptCallbackAddedT = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddCallbacksWithCancelation( - promise, - onResolve, onReject, onUnknownRejection, convertValue, TValue, - onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, promiseToPromiseT, - onCallbackAdded, onCallbackAddedConvert, onCallbackAddedT, - default(CancelationToken), default(CancelationToken), - onCancel, - onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedT, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedT, - configureAwaitType, configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddCallbacksWithCancelation( - promise, - onResolve, onReject, onUnknownRejection, convertValue, TValue, - onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, promiseToPromiseT, - onCallbackAdded, onCallbackAddedConvert, onCallbackAddedT, - cancelationSource.Token, cancelationSource.Token, - onCancel, - onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedT, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedT, - configureAwaitType, configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); - } + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, onReject, onUnknownRejection, convertValue, TValue, + onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, promiseToPromiseT, + onCallbackAdded, onCallbackAddedConvert, onCallbackAddedT, + default(CancelationToken), default(CancelationToken), + onCancel, + onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedT, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedT, + configureAwaitType, configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddCallbacksWithCancelation( + retainer.WaitAsync(), + onResolve, onReject, onUnknownRejection, convertValue, TValue, + onResolveCapture, onRejectCapture, onUnknownRejectionCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, promiseToPromiseT, + onCallbackAdded, onCallbackAddedConvert, onCallbackAddedT, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onDirectCallbackAdded, onDirectCallbackAddedConvert, onDirectCallbackAddedT, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, onAdoptCallbackAddedT, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } + } public static void AddCallbacksWithCancelation(Promise promise, Action onResolve = null, Action onReject = null, Action onUnknownRejection = null, TConvert convertValue = default(TConvert), T TValue = default(T), @@ -1753,780 +1858,780 @@ public static void AddCallbacksWithCancelation(P TestAction onAdoptCallbackAdded = null, TestAction, AdoptLocation> onAdoptCallbackAddedConvert = null, TestAction> onAdoptCallbackAddedT = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegates so no need for null check. - onResolve += x => { }; - onReject += s => { }; - onUnknownRejection += () => { }; - onResolveCapture += _ => { }; - onRejectCapture += _ => { }; - onUnknownRejectionCapture += _ => { }; - if (promiseToPromiseT == null) - { - promiseToPromiseT = _ => Promise.Resolved(TValue); - } - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) - { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedT == null) - { - onCallbackAddedT = (ref Promise p) => p.Forget(); - } - if (onDirectCallbackAdded == null) - { - onDirectCallbackAdded = (ref Promise p) => { }; - } - if (onDirectCallbackAddedConvert == null) - { - onDirectCallbackAddedConvert = (ref Promise p) => { }; - } - if (onDirectCallbackAddedT == null) - { - onDirectCallbackAddedT = (ref Promise p) => { }; - } - if (onAdoptCallbackAdded == null) - { - onAdoptCallbackAdded = (ref Promise p, AdoptLocation _) => { }; - } - if (onAdoptCallbackAddedConvert == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAddedConvert = (ref Promise p, AdoptLocation _) => { }; - } - if (onAdoptCallbackAddedT == null) - { - onAdoptCallbackAddedT = (ref Promise p) => { }; - } - onCancel += () => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p1); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p2); - onCallbackAdded(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, () => { onUnknownRejection(); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3, AdoptLocation.Reject); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p4); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p4, AdoptLocation.Reject); - onCallbackAdded(ref p4); - } + // Add empty delegates so no need for null check. + onResolve += x => { }; + onReject += s => { }; + onUnknownRejection += () => { }; + onResolveCapture += _ => { }; + onRejectCapture += _ => { }; + onUnknownRejectionCapture += _ => { }; + if (promiseToPromiseT == null) + { + promiseToPromiseT = _ => Promise.Resolved(TValue); + } + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedT == null) + { + onCallbackAddedT = (ref Promise p) => p.Forget(); + } + if (onDirectCallbackAdded == null) + { + onDirectCallbackAdded = (ref Promise p) => { }; + } + if (onDirectCallbackAddedConvert == null) + { + onDirectCallbackAddedConvert = (ref Promise p) => { }; + } + if (onDirectCallbackAddedT == null) + { + onDirectCallbackAddedT = (ref Promise p) => { }; + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p, AdoptLocation _) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p, AdoptLocation _) => { }; + } + if (onAdoptCallbackAddedT == null) + { + onAdoptCallbackAddedT = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p5); - onCallbackAddedConvert(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p6); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p7); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p7, AdoptLocation.Reject); - onCallbackAddedConvert(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8, AdoptLocation.Reject); - onCallbackAddedConvert(ref p8); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p1); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p2); + onCallbackAdded(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, () => { onUnknownRejection(); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3, AdoptLocation.Reject); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p4); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p4, AdoptLocation.Reject); + onCallbackAdded(ref p4); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p9 = default(Promise); - p9 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p9); }, () => { onUnknownRejection(); return promiseToPromise(p9); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p9, AdoptLocation.Both); - onCallbackAdded(ref p9); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p10 = default(Promise); - p10 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p10); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p10); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p10, AdoptLocation.Both); - onCallbackAdded(ref p10); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p11 = default(Promise); - p11 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p11); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p11, AdoptLocation.Resolve); - onCallbackAdded(ref p11); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p12 = default(Promise); - p12 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p12); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p12, AdoptLocation.Resolve); - onCallbackAdded(ref p12); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p5); + onCallbackAddedConvert(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p6); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p7); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p7, AdoptLocation.Reject); + onCallbackAddedConvert(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8, AdoptLocation.Reject); + onCallbackAddedConvert(ref p8); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p13 = default(Promise); - p13 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p13); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p13); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p13, AdoptLocation.Both); - onCallbackAddedConvert(ref p13); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p14 = default(Promise); - p14 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p14); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p14); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p14, AdoptLocation.Both); - onCallbackAddedConvert(ref p14); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p15 = default(Promise); - p15 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p15); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p15, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p15); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p16 = default(Promise); - p16 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p16); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p16, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p16); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p9 = default(Promise); + p9 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p9); }, () => { onUnknownRejection(); return promiseToPromise(p9); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p9, AdoptLocation.Both); + onCallbackAdded(ref p9); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p10 = default(Promise); + p10 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p10); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p10); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p10, AdoptLocation.Both); + onCallbackAdded(ref p10); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p11 = default(Promise); + p11 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p11); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p11, AdoptLocation.Resolve); + onCallbackAdded(ref p11); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p12 = default(Promise); + p12 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p12); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p12, AdoptLocation.Resolve); + onCallbackAdded(ref p12); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p13 = default(Promise); + p13 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p13); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p13); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p13, AdoptLocation.Both); + onCallbackAddedConvert(ref p13); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p14 = default(Promise); + p14 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p14); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p14); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p14, AdoptLocation.Both); + onCallbackAddedConvert(ref p14); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p15 = default(Promise); + p15 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p15); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p15, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p15); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p16 = default(Promise); + p16 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p16); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p16, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p16); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p17 = default(Promise); - p17 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(() => { onUnknownRejection(); return TValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onDirectCallbackAddedT(ref p17); - onCallbackAddedT(ref p17); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p18 = default(Promise); - p18 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch((TReject failValue) => { onReject(failValue); return TValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onDirectCallbackAddedT(ref p18); - onCallbackAddedT(ref p18); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p19 = default(Promise); - p19 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(() => { onUnknownRejection(); return promiseToPromiseT(p19); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onAdoptCallbackAddedT(ref p19); - onCallbackAddedT(ref p19); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p20 = default(Promise); - p20 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch((TReject failValue) => { onReject(failValue); return promiseToPromiseT(p20); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onAdoptCallbackAddedT(ref p20); - onCallbackAddedT(ref p20); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p17 = default(Promise); + p17 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(() => { onUnknownRejection(); return TValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onDirectCallbackAddedT(ref p17); + onCallbackAddedT(ref p17); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p18 = default(Promise); + p18 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch((TReject failValue) => { onReject(failValue); return TValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onDirectCallbackAddedT(ref p18); + onCallbackAddedT(ref p18); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p19 = default(Promise); + p19 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(() => { onUnknownRejection(); return promiseToPromiseT(p19); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onAdoptCallbackAddedT(ref p19); + onCallbackAddedT(ref p19); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p20 = default(Promise); + p20 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch((TReject failValue) => { onReject(failValue); return promiseToPromiseT(p20); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onAdoptCallbackAddedT(ref p20); + onCallbackAddedT(ref p20); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p21 = default(Promise); - p21 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p21); - onCallbackAdded(ref p21); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p22 = default(Promise); - p22 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p22); - onCallbackAdded(ref p22); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p23 = default(Promise); - p23 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p23); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p23, AdoptLocation.Reject); - onCallbackAdded(ref p23); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p24 = default(Promise); - p24 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p24); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p24, AdoptLocation.Reject); - onCallbackAdded(ref p24); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p25 = default(Promise); - p25 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p25); - onCallbackAddedConvert(ref p25); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p26 = default(Promise); - p26 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p26); - onCallbackAddedConvert(ref p26); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p27 = default(Promise); - p27 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p27); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p27, AdoptLocation.Reject); - onCallbackAddedConvert(ref p27); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p28 = default(Promise); - p28 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p28); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p28, AdoptLocation.Reject); - onCallbackAddedConvert(ref p28); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p21 = default(Promise); + p21 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p21); + onCallbackAdded(ref p21); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p22 = default(Promise); + p22 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p22); + onCallbackAdded(ref p22); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p23 = default(Promise); + p23 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p23); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p23, AdoptLocation.Reject); + onCallbackAdded(ref p23); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p24 = default(Promise); + p24 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p24); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p24, AdoptLocation.Reject); + onCallbackAdded(ref p24); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p29 = default(Promise); - p29 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p29); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p29); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p29, AdoptLocation.Both); - onCallbackAdded(ref p29); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p30 = default(Promise); - p30 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p30); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p30); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p30, AdoptLocation.Both); - onCallbackAdded(ref p30); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p31 = default(Promise); - p31 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p31); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p31, AdoptLocation.Resolve); - onCallbackAdded(ref p31); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p32 = default(Promise); - p32 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p32); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p32, AdoptLocation.Resolve); - onCallbackAdded(ref p32); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p25 = default(Promise); + p25 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p25); + onCallbackAddedConvert(ref p25); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p26 = default(Promise); + p26 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p26); + onCallbackAddedConvert(ref p26); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p27 = default(Promise); + p27 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p27); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p27, AdoptLocation.Reject); + onCallbackAddedConvert(ref p27); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p28 = default(Promise); + p28 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p28); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p28, AdoptLocation.Reject); + onCallbackAddedConvert(ref p28); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p29 = default(Promise); + p29 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p29); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p29); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p29, AdoptLocation.Both); + onCallbackAdded(ref p29); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p30 = default(Promise); + p30 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p30); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p30); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p30, AdoptLocation.Both); + onCallbackAdded(ref p30); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p31 = default(Promise); + p31 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p31); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p31, AdoptLocation.Resolve); + onCallbackAdded(ref p31); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p32 = default(Promise); + p32 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p32); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p32, AdoptLocation.Resolve); + onCallbackAdded(ref p32); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p33 = default(Promise); - p33 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p33); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p33); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p33, AdoptLocation.Both); - onCallbackAddedConvert(ref p33); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p34 = default(Promise); - p34 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p34); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p34); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p34, AdoptLocation.Both); - onCallbackAddedConvert(ref p34); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p35 = default(Promise); - p35 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p35); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p35, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p35); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p36 = default(Promise); - p36 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p36); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p36, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p36); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p33 = default(Promise); + p33 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p33); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p33); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p33, AdoptLocation.Both); + onCallbackAddedConvert(ref p33); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p34 = default(Promise); + p34 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p34); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p34); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p34, AdoptLocation.Both); + onCallbackAddedConvert(ref p34); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p35 = default(Promise); + p35 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p35); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p35, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p35); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p36 = default(Promise); + p36 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p36); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p36, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p36); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p37 = default(Promise); - p37 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return TValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onDirectCallbackAddedT(ref p37); - onCallbackAddedT(ref p37); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p38 = default(Promise); - p38 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return TValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onDirectCallbackAddedT(ref p38); - onCallbackAddedT(ref p38); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p39 = default(Promise); - p39 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseT(p39); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onAdoptCallbackAddedT(ref p39); - onCallbackAddedT(ref p39); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p40 = default(Promise); - p40 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseT(p40); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }); - onAdoptCallbackAddedT(ref p40); - onCallbackAddedT(ref p40); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p37 = default(Promise); + p37 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return TValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onDirectCallbackAddedT(ref p37); + onCallbackAddedT(ref p37); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p38 = default(Promise); + p38 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return TValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onDirectCallbackAddedT(ref p38); + onCallbackAddedT(ref p38); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p39 = default(Promise); + p39 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseT(p39); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onAdoptCallbackAddedT(ref p39); + onCallbackAddedT(ref p39); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p40 = default(Promise); + p40 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Catch(captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseT(p40); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }); + onAdoptCallbackAddedT(ref p40); + onCallbackAddedT(ref p40); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p41 = default(Promise); - p41 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p41); - onCallbackAdded(ref p41); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p42 = default(Promise); - p42 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p42); - onCallbackAdded(ref p42); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p43 = default(Promise); - p43 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, () => { onUnknownRejection(); return promiseToPromise(p43); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p43, AdoptLocation.Reject); - onCallbackAdded(ref p43); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p44 = default(Promise); - p44 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p44); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p44, AdoptLocation.Reject); - onCallbackAdded(ref p44); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p45 = default(Promise); - p45 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p45); - onCallbackAddedConvert(ref p45); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p46 = default(Promise); - p46 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p46); - onCallbackAddedConvert(ref p46); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p47 = default(Promise); - p47 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p47); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p47, AdoptLocation.Reject); - onCallbackAddedConvert(ref p47); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p48 = default(Promise); - p48 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p48); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p48, AdoptLocation.Reject); - onCallbackAddedConvert(ref p48); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p41 = default(Promise); + p41 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p41); + onCallbackAdded(ref p41); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p42 = default(Promise); + p42 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p42); + onCallbackAdded(ref p42); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p43 = default(Promise); + p43 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, () => { onUnknownRejection(); return promiseToPromise(p43); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p43, AdoptLocation.Reject); + onCallbackAdded(ref p43); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p44 = default(Promise); + p44 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p44); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p44, AdoptLocation.Reject); + onCallbackAdded(ref p44); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p49 = default(Promise); - p49 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p49); }, () => { onUnknownRejection(); return promiseToPromise(p49); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p49, AdoptLocation.Both); - onCallbackAdded(ref p49); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p50 = default(Promise); - p50 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p50); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p50); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p50, AdoptLocation.Both); - onCallbackAdded(ref p50); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p51 = default(Promise); - p51 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p51); }, () => { onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p51, AdoptLocation.Resolve); - onCallbackAdded(ref p51); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p52 = default(Promise); - p52 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p52); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p52, AdoptLocation.Resolve); - onCallbackAdded(ref p52); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p45 = default(Promise); + p45 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p45); + onCallbackAddedConvert(ref p45); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p46 = default(Promise); + p46 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p46); + onCallbackAddedConvert(ref p46); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p47 = default(Promise); + p47 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, () => { onUnknownRejection(); return promiseToPromiseConvert(p47); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p47, AdoptLocation.Reject); + onCallbackAddedConvert(ref p47); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p48 = default(Promise); + p48 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return convertValue; }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p48); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p48, AdoptLocation.Reject); + onCallbackAddedConvert(ref p48); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p53 = default(Promise); - p53 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p53); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p53); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p53, AdoptLocation.Both); - onCallbackAddedConvert(ref p53); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p54 = default(Promise); - p54 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p54); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p54); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p54, AdoptLocation.Both); - onCallbackAddedConvert(ref p54); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p55 = default(Promise); - p55 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p55); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p55, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p55); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p56 = default(Promise); - p56 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p56); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p56, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p56); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p49 = default(Promise); + p49 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p49); }, () => { onUnknownRejection(); return promiseToPromise(p49); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p49, AdoptLocation.Both); + onCallbackAdded(ref p49); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p50 = default(Promise); + p50 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p50); }, (TReject failValue) => { onReject(failValue); return promiseToPromise(p50); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p50, AdoptLocation.Both); + onCallbackAdded(ref p50); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p51 = default(Promise); + p51 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p51); }, () => { onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p51, AdoptLocation.Resolve); + onCallbackAdded(ref p51); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p52 = default(Promise); + p52 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromise(p52); }, (TReject failValue) => { onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p52, AdoptLocation.Resolve); + onCallbackAdded(ref p52); + } + + foreach (var p in GetTestablePromises(retainer)) + { + Promise p53 = default(Promise); + p53 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p53); }, () => { onUnknownRejection(); return promiseToPromiseConvert(p53); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p53, AdoptLocation.Both); + onCallbackAddedConvert(ref p53); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p54 = default(Promise); + p54 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p54); }, (TReject failValue) => { onReject(failValue); return promiseToPromiseConvert(p54); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p54, AdoptLocation.Both); + onCallbackAddedConvert(ref p54); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p55 = default(Promise); + p55 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p55); }, () => { onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p55, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p55); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p56 = default(Promise); + p56 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(captureValue, (cv, x) => { onResolveCapture(cv); onResolve(x); return promiseToPromiseConvert(p56); }, (TReject failValue) => { onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p56, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p56); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p57 = default(Promise); - p57 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p57); - onCallbackAdded(ref p57); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p58 = default(Promise); - p58 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onDirectCallbackAdded(ref p58); - onCallbackAdded(ref p58); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p59 = default(Promise); - p59 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p59); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p59, AdoptLocation.Reject); - onCallbackAdded(ref p59); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p60 = default(Promise); - p60 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p60); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p60, AdoptLocation.Reject); - onCallbackAdded(ref p60); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p57 = default(Promise); + p57 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p57); + onCallbackAdded(ref p57); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p58 = default(Promise); + p58 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onDirectCallbackAdded(ref p58); + onCallbackAdded(ref p58); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p59 = default(Promise); + p59 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p59); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p59, AdoptLocation.Reject); + onCallbackAdded(ref p59); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p60 = default(Promise); + p60 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p60); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p60, AdoptLocation.Reject); + onCallbackAdded(ref p60); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p61 = default(Promise); - p61 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p61); - onCallbackAddedConvert(ref p61); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p62 = default(Promise); - p62 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onDirectCallbackAddedConvert(ref p62); - onCallbackAddedConvert(ref p62); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p63 = default(Promise); - p63 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p63); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p63, AdoptLocation.Reject); - onCallbackAddedConvert(ref p63); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p64 = default(Promise); - p64 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p64); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p64, AdoptLocation.Reject); - onCallbackAddedConvert(ref p64); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p61 = default(Promise); + p61 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p61); + onCallbackAddedConvert(ref p61); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p62 = default(Promise); + p62 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onDirectCallbackAddedConvert(ref p62); + onCallbackAddedConvert(ref p62); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p63 = default(Promise); + p63 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p63); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p63, AdoptLocation.Reject); + onCallbackAddedConvert(ref p63); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p64 = default(Promise); + p64 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return convertValue; }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p64); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p64, AdoptLocation.Reject); + onCallbackAddedConvert(ref p64); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p65 = default(Promise); - p65 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p65); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p65); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p65, AdoptLocation.Both); - onCallbackAdded(ref p65); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p66 = default(Promise); - p66 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p66); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p66); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p66, AdoptLocation.Both); - onCallbackAdded(ref p66); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p67 = default(Promise); - p67 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p67); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p67, AdoptLocation.Resolve); - onCallbackAdded(ref p67); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p68 = default(Promise); - p68 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromise(p68); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p68, AdoptLocation.Resolve); - onCallbackAdded(ref p68); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p65 = default(Promise); + p65 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p65); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromise(p65); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p65, AdoptLocation.Both); + onCallbackAdded(ref p65); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p66 = default(Promise); + p66 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p66); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromise(p66); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p66, AdoptLocation.Both); + onCallbackAdded(ref p66); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p67 = default(Promise); + p67 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p67); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p67, AdoptLocation.Resolve); + onCallbackAdded(ref p67); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p68 = default(Promise); + p68 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromise(p68); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p68, AdoptLocation.Resolve); + onCallbackAdded(ref p68); + } - foreach (var p in GetTestablePromises(promise)) - { - Promise p69 = default(Promise); - p69 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p69); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p69); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p69, AdoptLocation.Both); - onCallbackAddedConvert(ref p69); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p70 = default(Promise); - p70 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p70); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p70); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p70, AdoptLocation.Both); - onCallbackAddedConvert(ref p70); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p71 = default(Promise); - p71 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p71); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p71, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p71); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p72 = default(Promise); - p72 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .Then(x => { onResolve(x); return promiseToPromiseConvert(p72); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p72, AdoptLocation.Resolve); - onCallbackAddedConvert(ref p72); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p69 = default(Promise); + p69 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p69); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return promiseToPromiseConvert(p69); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p69, AdoptLocation.Both); + onCallbackAddedConvert(ref p69); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p70 = default(Promise); + p70 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p70); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return promiseToPromiseConvert(p70); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p70, AdoptLocation.Both); + onCallbackAddedConvert(ref p70); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p71 = default(Promise); + p71 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p71); }, captureValue, cv => { onUnknownRejectionCapture(cv); onUnknownRejection(); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p71, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p71); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p72 = default(Promise); + p72 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .Then(x => { onResolve(x); return promiseToPromiseConvert(p72); }, captureValue, (TCapture cv, TReject failValue) => { onRejectCapture(cv); onReject(failValue); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p72, AdoptLocation.Resolve); + onCallbackAddedConvert(ref p72); + } } - - promise.Forget(); } public static void AddContinueCallbacks(Promise promise, Action onContinue = null, TConvert convertValue = default(TConvert), Action onContinueCapture = null, TCapture captureValue = default(TCapture), Func promiseToPromise = null, Func, Promise> promiseToPromiseConvert = null, - TestAction onCallbackAdded = null, TestAction> onCallbackAddedConvert = null, - Action onCancel = null, - TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, - ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) - { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddContinueCallbacksWithCancelation( - promise, - onContinue, convertValue, - onContinueCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - default(CancelationToken), default(CancelationToken), - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); + TestAction onCallbackAdded = null, TestAction> onCallbackAddedConvert = null, + Action onCancel = null, + TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, + ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) + { + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - CancelationSource cancelationSource = CancelationSource.New(); - AddContinueCallbacksWithCancelation( - promise, + AddContinueCallbacksWithCancelation( + retainer.WaitAsync(), onContinue, convertValue, onContinueCapture, captureValue, promiseToPromise, promiseToPromiseConvert, onCallbackAdded, onCallbackAddedConvert, - cancelationSource.Token, cancelationSource.Token, + default(CancelationToken), default(CancelationToken), onCancel, onAdoptCallbackAdded, onAdoptCallbackAddedConvert, configureAwaitType, configureAwaitForceAsync ); - cancelationSource.Dispose(); - promise.Forget(); + CancelationSource cancelationSource = CancelationSource.New(); + AddContinueCallbacksWithCancelation( + retainer.WaitAsync(), + onContinue, convertValue, + onContinueCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddContinueCallbacksWithCancelation(Promise promise, @@ -2539,109 +2644,109 @@ public static void AddContinueCallbacksWithCancelation(Promi TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegate so no need for null check. - onContinue += _ => { }; - onContinueCapture += (_, __) => { }; - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) - { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) - { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - if (onAdoptCallbackAddedConvert == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAddedConvert = (ref Promise p) => { }; - } - onCancel += () => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return promiseToPromiseConvert(p4); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p4); - onCallbackAddedConvert(ref p4); - } + // Add empty delegate so no need for null check. + onContinue += _ => { }; + onContinueCapture += (_, __) => { }; + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromise(p7); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p7); - onCallbackAdded(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8); - onCallbackAddedConvert(ref p8); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return promiseToPromiseConvert(p4); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p4); + onCallbackAddedConvert(ref p4); + } - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromise(p7); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p7); + onCallbackAdded(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8); + onCallbackAddedConvert(ref p8); + } + } } public static void AddContinueCallbacks(Promise promise, @@ -2653,36 +2758,36 @@ public static void AddContinueCallbacks(Promise promis TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddContinueCallbacksWithCancelation( - promise, - onContinue, convertValue, - onContinueCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - default(CancelationToken), default(CancelationToken), - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddContinueCallbacksWithCancelation( - promise, - onContinue, convertValue, - onContinueCapture, captureValue, - promiseToPromise, promiseToPromiseConvert, - onCallbackAdded, onCallbackAddedConvert, - cancelationSource.Token, cancelationSource.Token, - onCancel, - onAdoptCallbackAdded, onAdoptCallbackAddedConvert, - configureAwaitType, configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddContinueCallbacksWithCancelation( + retainer.WaitAsync(), + onContinue, convertValue, + onContinueCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + default(CancelationToken), default(CancelationToken), + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddContinueCallbacksWithCancelation( + retainer.WaitAsync(), + onContinue, convertValue, + onContinueCapture, captureValue, + promiseToPromise, promiseToPromiseConvert, + onCallbackAdded, onCallbackAddedConvert, + cancelationSource.Token, cancelationSource.Token, + onCancel, + onAdoptCallbackAdded, onAdoptCallbackAddedConvert, + configureAwaitType, configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddContinueCallbacksWithCancelation(Promise promise, @@ -2695,109 +2800,109 @@ public static void AddContinueCallbacksWithCancelation(Pr TestAction onAdoptCallbackAdded = null, TestAction> onAdoptCallbackAddedConvert = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegate so no need for null check. - onContinue += _ => { }; - onContinueCapture += (_, __) => { }; - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (promiseToPromiseConvert == null) - { - promiseToPromiseConvert = _ => Promise.Resolved(convertValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onCallbackAddedConvert == null) - { - onCallbackAddedConvert = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) - { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - if (onAdoptCallbackAddedConvert == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAddedConvert = (ref Promise p) => { }; - } - onCancel += () => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return promiseToPromise(p3); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p4 = default(Promise); - p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(r => { onContinue(r); return promiseToPromiseConvert(p4); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p4); - onCallbackAddedConvert(ref p4); - } + // Add empty delegate so no need for null check. + onContinue += _ => { }; + onContinueCapture += (_, __) => { }; + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (promiseToPromiseConvert == null) + { + promiseToPromiseConvert = _ => Promise.Resolved(convertValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onCallbackAddedConvert == null) + { + onCallbackAddedConvert = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + if (onAdoptCallbackAddedConvert == null) + { + onAdoptCallbackAddedConvert = (ref Promise p) => { }; + } + onCancel += () => { }; - foreach (var p in GetTestablePromises(promise)) - { - Promise p5 = default(Promise); - p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); }, cancelationToken) - .CatchCancelation(onCancel); - onCallbackAdded(ref p5); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p6 = default(Promise); - p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return convertValue; }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onCallbackAddedConvert(ref p6); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p7 = default(Promise); - p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromise(p7); }, cancelationToken) - .CatchCancelation(onCancel); - onAdoptCallbackAdded(ref p7); - onCallbackAdded(ref p7); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p8 = default(Promise); - p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromiseConvert(p8); }, cancelationToken) - .CatchCancelation(() => { onCancel(); return convertValue; }); - onAdoptCallbackAddedConvert(ref p8); - onCallbackAddedConvert(ref p8); - } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return promiseToPromise(p3); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p4 = default(Promise); + p4 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(r => { onContinue(r); return promiseToPromiseConvert(p4); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p4); + onCallbackAddedConvert(ref p4); + } - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p5 = default(Promise); + p5 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); }, cancelationToken) + .CatchCancelation(onCancel); + onCallbackAdded(ref p5); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p6 = default(Promise); + p6 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return convertValue; }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onCallbackAddedConvert(ref p6); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p7 = default(Promise); + p7 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromise(p7); }, cancelationToken) + .CatchCancelation(onCancel); + onAdoptCallbackAdded(ref p7); + onCallbackAdded(ref p7); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p8 = default(Promise); + p8 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .ContinueWith(captureValue, (cv, r) => { onContinueCapture(cv, r); onContinue(r); return promiseToPromiseConvert(p8); }, cancelationToken) + .CatchCancelation(() => { onCancel(); return convertValue; }); + onAdoptCallbackAddedConvert(ref p8); + onCallbackAddedConvert(ref p8); + } + } } public static void AddCancelCallbacks(Promise promise, @@ -2808,38 +2913,38 @@ public static void AddCancelCallbacks(Promise promise, TestAction onAdoptCallbackAdded = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddCancelCallbacks( - promise, - default(CancelationToken), - onCancel, onCancelCapture, - captureValue, - promiseToPromise, - onCallbackAdded, - onAdoptCallbackAdded, - default(CancelationToken), - configureAwaitType, - configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddCancelCallbacks( - promise, - cancelationSource.Token, - onCancel, onCancelCapture, - captureValue, - promiseToPromise, - onCallbackAdded, - onAdoptCallbackAdded, - cancelationSource.Token, - configureAwaitType, - configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddCancelCallbacks( + retainer.WaitAsync(), + default(CancelationToken), + onCancel, onCancelCapture, + captureValue, + promiseToPromise, + onCallbackAdded, + onAdoptCallbackAdded, + default(CancelationToken), + configureAwaitType, + configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddCancelCallbacks( + retainer.WaitAsync(), + cancelationSource.Token, + onCancel, onCancelCapture, + captureValue, + promiseToPromise, + onCallbackAdded, + onAdoptCallbackAdded, + cancelationSource.Token, + configureAwaitType, + configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddCancelCallbacks(Promise promise, @@ -2852,57 +2957,57 @@ public static void AddCancelCallbacks(Promise promise, CancelationToken waitAsyncCancelationToken = default(CancelationToken), ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegate so no need for null check. - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - onCancel += () => { }; - onCancelCapture += cv => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, cancelationToken) - .CatchCancelation(() => { onCancel(); }, cancelationToken); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(captureValue, cv => { onCancelCapture(cv); }, cancelationToken); - onCallbackAdded(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(() => { onCancel(); return promiseToPromise(p3); }, cancelationToken); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return promiseToPromise(p3); }, cancelationToken); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } + // Add empty delegate so no need for null check. + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + onCancel += () => { }; + onCancelCapture += cv => { }; - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, cancelationToken) + .CatchCancelation(() => { onCancel(); }, cancelationToken); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(captureValue, cv => { onCancelCapture(cv); }, cancelationToken); + onCallbackAdded(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(() => { onCancel(); return promiseToPromise(p3); }, cancelationToken); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return promiseToPromise(p3); }, cancelationToken); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + } } public static void AddCancelCallbacks(Promise promise, T TValue = default(T), @@ -2913,38 +3018,38 @@ public static void AddCancelCallbacks(Promise promise, TestAction> onAdoptCallbackAdded = null, ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - AddCancelCallbacks( - promise, - default(CancelationToken), TValue, - onCancel, onCancelCapture, - captureValue, - promiseToPromise, - onCallbackAdded, - onAdoptCallbackAdded, - default(CancelationToken), - configureAwaitType, - configureAwaitForceAsync - ); - - CancelationSource cancelationSource = CancelationSource.New(); - AddCancelCallbacks( - promise, - cancelationSource.Token, TValue, - onCancel, onCancelCapture, - captureValue, - promiseToPromise, - onCallbackAdded, - onAdoptCallbackAdded, - cancelationSource.Token, - configureAwaitType, - configureAwaitForceAsync - ); - cancelationSource.Dispose(); - - promise.Forget(); + using (var retainer = promise.GetRetainer()) + { + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. + + AddCancelCallbacks( + retainer.WaitAsync(), + default(CancelationToken), TValue, + onCancel, onCancelCapture, + captureValue, + promiseToPromise, + onCallbackAdded, + onAdoptCallbackAdded, + default(CancelationToken), + configureAwaitType, + configureAwaitForceAsync + ); + + CancelationSource cancelationSource = CancelationSource.New(); + AddCancelCallbacks( + retainer.WaitAsync(), + cancelationSource.Token, TValue, + onCancel, onCancelCapture, + captureValue, + promiseToPromise, + onCallbackAdded, + onAdoptCallbackAdded, + cancelationSource.Token, + configureAwaitType, + configureAwaitForceAsync + ); + cancelationSource.Dispose(); + } } public static void AddCancelCallbacks(Promise promise, @@ -2957,57 +3062,57 @@ public static void AddCancelCallbacks(Promise promise, CancelationToken waitAsyncCancelationToken = default(CancelationToken), ConfigureAwaitType configureAwaitType = ConfigureAwaitType.None, bool configureAwaitForceAsync = false) { - promise = promise.Preserve(); - promise.Catch(() => { }).Forget(); // Suppress any rejections from the preserved promise. - - // Add empty delegate so no need for null check. - if (promiseToPromise == null) - { - promiseToPromise = _ => Promise.Resolved(TValue); - } - if (onCallbackAdded == null) - { - onCallbackAdded = (ref Promise p) => p.Forget(); - } - if (onAdoptCallbackAdded == null) + using (var retainer = promise.GetRetainer()) { - onAdoptCallbackAdded = (ref Promise p) => { }; - } - onCancel += () => { }; - onCancelCapture += cv => { }; + retainer.WaitAsync().Catch(() => { }).Forget(); // Suppress any rejections from the retained promise. - foreach (var p in GetTestablePromises(promise)) - { - Promise p1 = default(Promise); - p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(() => { onCancel(); return TValue; }, cancelationToken); - onCallbackAdded(ref p1); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p2 = default(Promise); - p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return TValue; }, cancelationToken); - onCallbackAdded(ref p2); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(() => { onCancel(); return promiseToPromise(p3); }, cancelationToken); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } - foreach (var p in GetTestablePromises(promise)) - { - Promise p3 = default(Promise); - p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) - .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return promiseToPromise(p3); }, cancelationToken); - onAdoptCallbackAdded(ref p3); - onCallbackAdded(ref p3); - } + // Add empty delegate so no need for null check. + if (promiseToPromise == null) + { + promiseToPromise = _ => Promise.Resolved(TValue); + } + if (onCallbackAdded == null) + { + onCallbackAdded = (ref Promise p) => p.Forget(); + } + if (onAdoptCallbackAdded == null) + { + onAdoptCallbackAdded = (ref Promise p) => { }; + } + onCancel += () => { }; + onCancelCapture += cv => { }; - promise.Forget(); + foreach (var p in GetTestablePromises(retainer)) + { + Promise p1 = default(Promise); + p1 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(() => { onCancel(); return TValue; }, cancelationToken); + onCallbackAdded(ref p1); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p2 = default(Promise); + p2 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return TValue; }, cancelationToken); + onCallbackAdded(ref p2); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(() => { onCancel(); return promiseToPromise(p3); }, cancelationToken); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + foreach (var p in GetTestablePromises(retainer)) + { + Promise p3 = default(Promise); + p3 = p.ConfigureAwait(configureAwaitType, configureAwaitForceAsync, waitAsyncCancelationToken) + .CatchCancelation(captureValue, cv => { onCancelCapture(cv); return promiseToPromise(p3); }, cancelationToken); + onAdoptCallbackAdded(ref p3); + onCallbackAdded(ref p3); + } + } } public static Action[] ResolveActionsVoid(Action onResolved = null) @@ -3692,5 +3797,235 @@ public static Func>[] ActionsReturningPromiseT(Func> re () => Promise.Resolved(1).ContinueWith(1, (cv, _) => returnProvider()), }; } + + public static (Func func, AdoptLocation adoptLocation)[] GetFunctionsAdoptingPromiseVoidToVoid(Func returnProvider) + { + return new (Func, AdoptLocation)[] + { + (promise => promise.Then(() => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider()), AdoptLocation.Resolve), + + + (promise => promise.Then(() => returnProvider(), () => { }), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), (TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(() => { }, () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => { }, (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => returnProvider(), () => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => { }, () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => { }, (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => returnProvider(), 1, cv => { }), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => { }, 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => { }, 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => returnProvider(), 1, cv => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => { }, 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => { }, 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + + (promise => promise.Catch(() => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch((TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + + + (promise => promise.ContinueWith(_ => returnProvider()), AdoptLocation.Both), + (promise => promise.ContinueWith(1, (cv, _) => returnProvider()), AdoptLocation.Both), + }; + } + + public static (Func, Promise> func, AdoptLocation adoptLocation)[] GetFunctionsAdoptingPromiseTToVoid(Func returnProvider) + { + return new (Func, Promise> func, AdoptLocation)[] + { + (promise => promise.Then(v => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(v => returnProvider(), () => { }), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), (TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), () => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(1, (cv, v) => returnProvider(), () => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), (TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, (cv, v) => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(v => returnProvider(), 1, cv => { }), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), 1, (int cv, TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(v => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, cv => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, (int cv, TCatch r) => { }), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.ContinueWith(_ => returnProvider()), AdoptLocation.Both), + (promise => promise.ContinueWith(1, (cv, _) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => { }, () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => { }, (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => { }, () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => { }, (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => { }, 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => { }, 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => { }, 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => { }, 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + + (promise => promise.Catch(() => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch((TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + }; + } + + public static (Func> func, AdoptLocation adoptLocation)[] GetFunctionsAdoptingPromiseVoidToT(Func> returnProvider) + { + return new (Func> func, AdoptLocation adoptLocation)[] + { + (promise => promise.Then(() => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider()), AdoptLocation.Resolve), + + + (promise => promise.Then(() => returnProvider(), () => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), (TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(() => default(T), () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => default(T), (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => returnProvider(), () => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => default(T), () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => default(T), (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => returnProvider(), 1, cv => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => default(T), 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => default(T), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => returnProvider(), 1, cv => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => default(T), 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => default(T), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + + (promise => promise.ContinueWith(_ => returnProvider()), AdoptLocation.Both), + (promise => promise.ContinueWith(1, (cv, _) => returnProvider()), AdoptLocation.Both), + }; + } + + public static (Func, Promise> func, AdoptLocation adoptLocation)[] GetFunctionsAdoptingPromiseTToT(Func> returnProvider) + { + return new (Func, Promise> func, AdoptLocation adoptLocation)[] + { + (promise => promise.Then(v => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(v => returnProvider(), () => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), (TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), () => returnProvider()), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Resolve), + + (promise => promise.Then(1, (cv, v) => returnProvider(), () => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), (TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, (cv, v) => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(v => returnProvider(), 1, cv => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), 1, (int cv, TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(v => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(v => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, cv => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, (int cv, TCatch r) => default(T)), AdoptLocation.Resolve), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, (cv, v) => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.ContinueWith(_ => returnProvider()), AdoptLocation.Both), + (promise => promise.ContinueWith(1, (cv, _) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => default(T), () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => default(T), (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => default(T), () => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => default(T), (TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), () => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), (TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(() => default(T), 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => default(T), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(() => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(() => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + (promise => promise.Then(1, cv => default(T), 1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => default(T), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Then(1, cv => returnProvider(), 1, cv => returnProvider()), AdoptLocation.Both), + (promise => promise.Then(1, cv => returnProvider(), 1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Both), + + + (promise => promise.Catch(() => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch((TCatch r) => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, cv => returnProvider()), AdoptLocation.Reject), + (promise => promise.Catch(1, (int cv, TCatch r) => returnProvider()), AdoptLocation.Reject), + }; + } } } \ No newline at end of file diff --git a/Package/Tests/Helpers/ThreadHelper.cs b/Package/Tests/Helpers/ThreadHelper.cs index 2cc1a986..6d24d563 100644 --- a/Package/Tests/Helpers/ThreadHelper.cs +++ b/Package/Tests/Helpers/ThreadHelper.cs @@ -1,6 +1,7 @@ using Proto.Promises; using System; using System.Collections.Generic; +using System.Runtime.ExceptionServices; using System.Threading; namespace ProtoPromiseTests.Concurrency @@ -81,7 +82,7 @@ private void Execute() catch (Exception e) { // Only reporting one exception instead of aggregate. - merger._executionException = e; + merger._executionExceptionInfo = ExceptionDispatchInfo.Capture(e); } Interlocked.Decrement(ref merger._currentParticipants); } @@ -94,7 +95,7 @@ private sealed class ThreadMerger volatile public int _currentParticipants = 0; // Only reporting one exception instead of aggregate. // This is so we don't have to wait on all actions to complete when 1 has errored, and also so we don't overload the test error output. - volatile public Exception _executionException = null; + volatile public ExceptionDispatchInfo _executionExceptionInfo = null; } public static readonly int multiExecutionCount = Math.Min(Environment.ProcessorCount * 100, 32766); // Maximum participants Barrier allows is 32767 (15 bits), subtract 1 for main/test thread. @@ -181,12 +182,8 @@ public void AddParallelActionSetup(Action action, int offset = 0) int numActions = merger._currentParticipants; merger._barrier.SignalAndWait(); TimeSpan timeout = TimeSpan.FromTicks(timeoutPerAction.Ticks * numActions); - bool timedOut = !SpinWait.SpinUntil(() => merger._currentParticipants <= 0 || merger._executionException != null, timeout); - if (merger._executionException != null) - { - // Preserve the stacktrace. - throw new Exception("", merger._executionException); - } + bool timedOut = !SpinWait.SpinUntil(() => merger._currentParticipants <= 0 || merger._executionExceptionInfo != null, timeout); + merger._executionExceptionInfo?.Throw(); if (timedOut) { throw new TimeoutException(numActions + " Action(s) timed out after " + timeout + ", there may be a deadlock. Remaining Actions: " + merger._currentParticipants); diff --git a/Package/UnityHelpers/2018.3/Internal/PromiseBehaviour.cs b/Package/UnityHelpers/2018.3/Internal/PromiseBehaviour.cs index 11c455c2..7d186cee 100644 --- a/Package/UnityHelpers/2018.3/Internal/PromiseBehaviour.cs +++ b/Package/UnityHelpers/2018.3/Internal/PromiseBehaviour.cs @@ -16,6 +16,13 @@ namespace Proto.Promises #endif internal static partial class InternalHelper { + // AppDomain reload could be disabled in editor, so we need to explicitly reset static fields. + // See https://github.com/timcassell/ProtoPromise/issues/204 + // https://docs.unity3d.com/Manual/DomainReloading.html + [RuntimeInitializeOnLoadMethod((RuntimeInitializeLoadType) 4)] // SubsystemRegistration + internal static void ResetStaticState() + => PromiseBehaviour.ResetStaticState(); + // We initialize the config as early as possible. Ideally we would just do this in static constructors of Promise() and Promise.Config, // but since this is in a separate assembly, that's not possible. // Also, using static constructors would slightly slow down promises in IL2CPP where it would have to check if it already ran on every call. @@ -24,13 +31,6 @@ internal static partial class InternalHelper internal static void InitializePromiseConfig() => PromiseBehaviour.Initialize(); - // AppDomain reload could be disabled in editor, so we need to explicitly reset static fields. - // See https://github.com/timcassell/ProtoPromise/issues/204 - // https://docs.unity3d.com/Manual/DomainReloading.html - [RuntimeInitializeOnLoadMethod((RuntimeInitializeLoadType) 4)] // SubsystemRegistration - internal static void ResetStaticState() - => PromiseBehaviour.ResetStaticState(); - #if !PROTO_PROMISE_DEVELOPER_MODE [DebuggerNonUserCode, StackTraceHidden] #endif @@ -96,9 +96,6 @@ private void Start() Init(); } - static partial void StaticInit(); - partial void Init(); - // This should never be called except when the application is shutting down. // Users would have to go out of their way to find and destroy the PromiseBehaviour instance. private void OnDestroy() @@ -106,7 +103,6 @@ private void OnDestroy() if (!_isApplicationQuitting & s_instance == this) { UnityEngine.Debug.LogError("PromiseBehaviour destroyed! Removing PromiseSynchronizationContext from Promise.Config.ForegroundContext. PromiseYielder functions will stop working."); - ResetConfig(); ResetStaticState(); } } @@ -115,7 +111,7 @@ private void HandleRejection(UnhandledException exception) { // We report uncaught rejections in Update instead of directly sending them to UnityEngine.Debug.LogException, // so that we can minimize the extra stack frames in the logs that we don't care about. - lock (_unhandledExceptions) + lock (this) { _unhandledExceptions.Enqueue(exception); } @@ -134,8 +130,7 @@ private void Update() } // Pop and pass to UnityEngine.Debug here so Unity won't add extra stackframes that we don't care about. - object locker = _unhandledExceptions; - lock (locker) + lock (this) { (_currentlyReportingExceptions, _unhandledExceptions) = (_unhandledExceptions, _currentlyReportingExceptions); } @@ -150,20 +145,29 @@ private void Update() ProcessUpdate(); } - partial void ProcessUpdate(); private void OnApplicationQuit() { _isApplicationQuitting = true; if (Application.isEditor & s_instance == this) { + // Reset the SynchronizationContext.Current to Unity's context so it will work in edit mode. + // We don't set the Promise.Config contexts to the same, because we only have this working with UnityEngine, + // not UnityEditor, so we can't set it on project load. + // Users can setup the Promise.Config themselves if they need the functionality in edit mode. + + // Also, we keep the UncaughtRejectionHandler routed to here which will never have its Update ran again, which effectively suppresses any further exceptions, + // in case any background threads are left running after play mode exited, and in case promise objects were left un-released or un-completed and their finalizers ran (very likely). + if (SynchronizationContext.Current == _syncContext) + { + SynchronizationContext.SetSynchronizationContext(_oldContext); + } + // Destroy this to prevent a memory leak. Destroy(this); } } - partial void ResetProcessors(); - private void ResetConfig() { if (Promise.Config.ForegroundContext == _syncContext) @@ -186,12 +190,14 @@ private void ResetConfig() internal static void ResetStaticState() { - if (s_instance is object) + if (s_instance is null) { - s_instance.ResetProcessors(); - s_instance.ResetConfig(); - s_instance = null; + return; } + + s_instance.ResetProcessors(); + s_instance.ResetConfig(); + s_instance = null; } } } diff --git a/Package/UnityHelpers/2018.3/Internal/PromiseYielderInternal.cs b/Package/UnityHelpers/2018.3/Internal/PromiseYielderInternal.cs index e94702ba..725d67de 100644 --- a/Package/UnityHelpers/2018.3/Internal/PromiseYielderInternal.cs +++ b/Package/UnityHelpers/2018.3/Internal/PromiseYielderInternal.cs @@ -38,10 +38,12 @@ internal static void ValidateIsOnMainThread(int skipFrames) } #else [MethodImpl(Internal.InlineOption)] +#pragma warning disable IDE0060 // Remove unused parameter internal static void ValidateIsOnMainThread(int skipFrames) +#pragma warning restore IDE0060 // Remove unused parameter { // We read Time.frameCount in RELEASE mode since it's a faster thread check than accessing Thread.CurrentThread. - var _ = Time.frameCount; + _ = Time.frameCount; } #endif @@ -71,20 +73,20 @@ private static void SetTimeValues() s_deltaTime = Time.deltaTime; } - static partial void StaticInit() + static private void StaticInit() { s_mainThread = Thread.CurrentThread; SetTimeValues(); } - partial void Init() + private void Init() { StartCoroutine(UpdateRoutine()); StartCoroutine(FixedUpdateRoutine()); StartCoroutine(EndOfFrameRoutine()); } - partial void ResetProcessors() + private void ResetProcessors() { s_waitOneFrameProcessor.Clear(); s_updateProcessor.Clear(); @@ -106,7 +108,7 @@ private IEnumerator UpdateRoutine() } // This is called from Update after the synchronization context is executed. - partial void ProcessUpdate() + private void ProcessUpdate() => s_updateProcessor.Process(); private void LateUpdate() @@ -320,7 +322,7 @@ internal InstructionProcessorGroup(int initialProcessorCapacity) } [MethodImpl(Internal.InlineOption)] - internal void WaitFor(ref TYieldInstruction instruction) where TYieldInstruction : struct, IYieldInstruction + internal void WaitFor(in TYieldInstruction instruction) where TYieldInstruction : struct, IYieldInstruction { #if PROMISE_DEBUG || PROTO_PROMISE_DEVELOPER_MODE ValidateIsOnMainThread(2); @@ -334,7 +336,7 @@ internal void WaitFor(ref TYieldInstruction instruction) wher _processors.Add(processor); } - processor.WaitFor(ref instruction); + processor.WaitFor(instruction); } [MethodImpl(Internal.InlineOption)] @@ -391,7 +393,7 @@ internal InstructionProcessor(int initialCapacity) } [MethodImpl(Internal.InlineOption)] - internal void WaitFor(ref TYieldInstruction instruction) + internal void WaitFor(in TYieldInstruction instruction) { int capacity; if (Time.frameCount != PromiseBehaviour.s_currentFrame) @@ -447,30 +449,23 @@ internal override void Process() for (int i = 0; i < max; ++i) { - // ref locals are not available in older C# versions, so we pass it to a function with aggressive inlining instead. - Evaluate(ref current[i]); - --_currentCount; - } - Array.Clear(_currentQueue, 0, max); - } - - [MethodImpl(Internal.InlineOption)] - private void Evaluate(ref TYieldInstruction instruction) - { - // If any instruction throws, we still need to execute the remaining instructions. - try - { - if (!instruction.Evaluate()) + ref TYieldInstruction instruction = ref current[i]; + // If any instruction throws, we still need to execute the remaining instructions. + try { - // This is hottest path, so we don't do a bounds check here (see WaitFor). - _nextQueue[_nextCount] = instruction; - ++_nextCount; + if (!instruction.Evaluate()) + { + // This is hottest path, so we don't do a bounds check here (see WaitFor). + _nextQueue[_nextCount++] = instruction; + } } + catch (Exception e) + { + UnityEngine.Debug.LogException(e); + } + --_currentCount; } - catch (Exception e) - { - UnityEngine.Debug.LogException(e); - } + Array.Clear(_currentQueue, 0, max); } internal override void Reset() @@ -551,7 +546,7 @@ private void OnCancel(MonoBehaviour runner) return; } // The token could be canceled from any thread, so we have to dispatch the cancelation logic to the main thread. - Promise.Run(ValueTuple.Create(this, runner, _id), (cv) => cv.Item1.OnCancel(cv.Item2, cv.Item3), + Promise.Run((this, runner, _id), (cv) => cv.Item1.OnCancel(cv.runner, cv._id), PromiseBehaviour.Instance._syncContext) // Explicitly use the sync context in case the user overwrites the Config.ForegroundContext. .Forget(); } diff --git a/Package/UnityHelpers/2018.3/PromiseYieldExtensions.cs b/Package/UnityHelpers/2018.3/PromiseYieldExtensions.cs index 07684c90..14ce2c50 100644 --- a/Package/UnityHelpers/2018.3/PromiseYieldExtensions.cs +++ b/Package/UnityHelpers/2018.3/PromiseYieldExtensions.cs @@ -10,6 +10,7 @@ using System.Runtime.ExceptionServices; #pragma warning disable IDE0090 // Use 'new(...)' +#pragma warning disable IDE0251 // Make member 'readonly' namespace Proto.Promises { @@ -135,7 +136,7 @@ public void OnCompleted(Action continuation) InternalHelper.ValidateIsOnMainThread(1); var copy = this; copy._continuation = continuation; - InternalHelper.PromiseBehaviour.Instance._updateProcessor.WaitFor(ref copy); + InternalHelper.PromiseBehaviour.Instance._updateProcessor.WaitFor(copy); } /// diff --git a/Package/UnityHelpers/2018.3/PromiseYieldInstruction.cs b/Package/UnityHelpers/2018.3/PromiseYieldInstruction.cs index 8f55f030..ea25e65e 100644 --- a/Package/UnityHelpers/2018.3/PromiseYieldInstruction.cs +++ b/Package/UnityHelpers/2018.3/PromiseYieldInstruction.cs @@ -87,7 +87,7 @@ public void GetResult() } case Promise.State.Canceled: { - throw Internal.CanceledExceptionInternal.GetOrCreate(); + throw Promise.CancelException(); } case Promise.State.Rejected: { diff --git a/Package/package.json b/Package/package.json index ec5b5e84..3354b402 100644 --- a/Package/package.json +++ b/Package/package.json @@ -1,6 +1,6 @@ { "name": "com.timcassell.protopromise", - "version": "3.0.2", + "version": "3.1.0", "displayName": "ProtoPromise", "description": "Robust and efficient library for management of asynchronous operations.", "changelogUrl": "https://github.com/timcassell/ProtoPromise/tree/master/Docs/Changelog", diff --git a/ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj b/ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj index 39175bc3..b7c23aea 100644 --- a/ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj +++ b/ProtoPromise.Analyzer/ProtoPromise.Analyzer.Test/ProtoPromise.Analyzer.Test.csproj @@ -1,7 +1,7 @@ - net6.0;net472 + net8.0;net48 true true diff --git a/ProtoPromise.UnityHelpers/2018.3/ProtoPromise.UnityHelpers.2018.3.csproj b/ProtoPromise.UnityHelpers/2018.3/ProtoPromise.UnityHelpers.2018.3.csproj index b4da6ae8..2a2b8a60 100644 --- a/ProtoPromise.UnityHelpers/2018.3/ProtoPromise.UnityHelpers.2018.3.csproj +++ b/ProtoPromise.UnityHelpers/2018.3/ProtoPromise.UnityHelpers.2018.3.csproj @@ -3,7 +3,7 @@ netstandard2.0;netstandard2.1 - 3.0.2 + 3.1.0 $(DefineConstants);ENABLE_IL2CPP diff --git a/ProtoPromise.UnityHelpers/2023.1/ProtoPromise.UnityHelpers.2023.1.csproj b/ProtoPromise.UnityHelpers/2023.1/ProtoPromise.UnityHelpers.2023.1.csproj index 1e00c720..1ee0d99b 100644 --- a/ProtoPromise.UnityHelpers/2023.1/ProtoPromise.UnityHelpers.2023.1.csproj +++ b/ProtoPromise.UnityHelpers/2023.1/ProtoPromise.UnityHelpers.2023.1.csproj @@ -2,7 +2,7 @@ netstandard2.1 - 3.0.2 + 3.1.0 9 diff --git a/ProtoPromise/ProtoPromise.csproj b/ProtoPromise/ProtoPromise.csproj index 1b78e331..dac7fcf4 100644 --- a/ProtoPromise/ProtoPromise.csproj +++ b/ProtoPromise/ProtoPromise.csproj @@ -2,7 +2,7 @@ netstandard2.0;netstandard2.1;net6.0 - 3.0.2 + 3.1.0 false diff --git a/ProtoPromise/nuget/readme.md b/ProtoPromise/nuget/readme.md index 61bf79f6..49c7beb4 100644 --- a/ProtoPromise/nuget/readme.md +++ b/ProtoPromise/nuget/readme.md @@ -4,12 +4,12 @@ Robust and efficient library for management of asynchronous operations. - Cancelable operations with custom allocation-free CancelationToken/Source - Allocation-free async iterators with async Linq - Progress with enforced normalization +- Structured concurrency - async/await support and .Then API - Thread safe -- Full causality traces - Easily switch to foreground or background context -- Combine async operations - Circular await detection +- Full causality traces - Interoperable with Tasks - CLS compliant diff --git a/ProtoPromise/nuget/targets/net6.0/ProtoPromise.targets b/ProtoPromise/nuget/targets/net6.0/ProtoPromise.targets index 2ae06387..176db1f3 100644 --- a/ProtoPromise/nuget/targets/net6.0/ProtoPromise.targets +++ b/ProtoPromise/nuget/targets/net6.0/ProtoPromise.targets @@ -1,19 +1,23 @@ + + - - - $(MSBuildThisFileDirectory)..\..\lib\net6.0\Release\ProtoPromise.dll - - + + Release + - - - $(MSBuildThisFileDirectory)..\..\lib\net6.0\Debug\ProtoPromise.dll - - + + Debug + + + + + $(MSBuildThisFileDirectory)..\..\lib\net6.0\$(ProtoPromiseConfiguration)\ProtoPromise.dll + + \ No newline at end of file diff --git a/ProtoPromise/nuget/targets/netstandard2.0/ProtoPromise.targets b/ProtoPromise/nuget/targets/netstandard2.0/ProtoPromise.targets index 1ef427c5..d38c7763 100644 --- a/ProtoPromise/nuget/targets/netstandard2.0/ProtoPromise.targets +++ b/ProtoPromise/nuget/targets/netstandard2.0/ProtoPromise.targets @@ -1,19 +1,23 @@ + + - - - $(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Release\ProtoPromise.dll - - + + Release + - - - $(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\Debug\ProtoPromise.dll - - + + Debug + + + + + $(MSBuildThisFileDirectory)..\..\lib\netstandard2.0\$(ProtoPromiseConfiguration)\ProtoPromise.dll + + \ No newline at end of file diff --git a/ProtoPromise/nuget/targets/netstandard2.1/ProtoPromise.targets b/ProtoPromise/nuget/targets/netstandard2.1/ProtoPromise.targets index 75a9d092..6680cc49 100644 --- a/ProtoPromise/nuget/targets/netstandard2.1/ProtoPromise.targets +++ b/ProtoPromise/nuget/targets/netstandard2.1/ProtoPromise.targets @@ -1,19 +1,23 @@ + + - - - $(MSBuildThisFileDirectory)..\..\lib\netstandard2.1\Release\ProtoPromise.dll - - + + Release + - - - $(MSBuildThisFileDirectory)..\..\lib\netstandard2.1\Debug\ProtoPromise.dll - - + + Debug + + + + + $(MSBuildThisFileDirectory)..\..\lib\netstandard2.1\$(ProtoPromiseConfiguration)\ProtoPromise.dll + + \ No newline at end of file diff --git a/ProtoPromiseTests/ProtoPromiseTests.csproj b/ProtoPromiseTests/ProtoPromiseTests.csproj index 16eaf0ab..9566bfba 100644 --- a/ProtoPromiseTests/ProtoPromiseTests.csproj +++ b/ProtoPromiseTests/ProtoPromiseTests.csproj @@ -1,7 +1,7 @@  - net6.0;net472 + net8.0;net48 false 9 diff --git a/ProtoPromise_Unity/.gitignore b/ProtoPromise_Unity/.gitignore index 7bbb5ee3..1f61d18b 100644 --- a/ProtoPromise_Unity/.gitignore +++ b/ProtoPromise_Unity/.gitignore @@ -162,9 +162,10 @@ PublishScripts/ *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* -!Packages/manifest.json -# except build/, which is used as an MSBuild target. +# except build/, which is used as an MSBuild target !**/packages/build/ +# and Unity's manifest.json. +!**/Packages/manifest.json # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files diff --git a/ProtoPromise_Unity/Packages/manifest.json b/ProtoPromise_Unity/Packages/manifest.json index 2880a44c..0c11ed77 100644 --- a/ProtoPromise_Unity/Packages/manifest.json +++ b/ProtoPromise_Unity/Packages/manifest.json @@ -1,42 +1,13 @@ { "dependencies": { "com.timcassell.protopromise": "file:../../Package", - "com.unity.ads": "2.0.8", - "com.unity.analytics": "3.2.3", - "com.unity.collab-proxy": "1.2.15", "com.unity.package-manager-ui": "2.0.13", - "com.unity.purchasing": "2.2.1", "com.unity.textmeshpro": "1.4.1", - "com.unity.modules.ai": "1.0.0", - "com.unity.modules.animation": "1.0.0", - "com.unity.modules.assetbundle": "1.0.0", - "com.unity.modules.audio": "1.0.0", - "com.unity.modules.cloth": "1.0.0", - "com.unity.modules.director": "1.0.0", "com.unity.modules.imageconversion": "1.0.0", "com.unity.modules.imgui": "1.0.0", - "com.unity.modules.jsonserialize": "1.0.0", - "com.unity.modules.particlesystem": "1.0.0", - "com.unity.modules.physics": "1.0.0", - "com.unity.modules.physics2d": "1.0.0", - "com.unity.modules.screencapture": "1.0.0", - "com.unity.modules.terrain": "1.0.0", - "com.unity.modules.terrainphysics": "1.0.0", - "com.unity.modules.tilemap": "1.0.0", "com.unity.modules.ui": "1.0.0", - "com.unity.modules.uielements": "1.0.0", - "com.unity.modules.umbra": "1.0.0", - "com.unity.modules.unityanalytics": "1.0.0", "com.unity.modules.unitywebrequest": "1.0.0", - "com.unity.modules.unitywebrequestassetbundle": "1.0.0", - "com.unity.modules.unitywebrequestaudio": "1.0.0", - "com.unity.modules.unitywebrequesttexture": "1.0.0", - "com.unity.modules.unitywebrequestwww": "1.0.0", - "com.unity.modules.vehicles": "1.0.0", - "com.unity.modules.video": "1.0.0", - "com.unity.modules.vr": "1.0.0", - "com.unity.modules.wind": "1.0.0", - "com.unity.modules.xr": "1.0.0" + "com.unity.modules.unitywebrequesttexture": "1.0.0" }, "testables": [ "com.timcassell.protopromise" diff --git a/global.json b/global.json deleted file mode 100644 index 30762386..00000000 --- a/global.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "sdk": { - "version": "6.0.105" - } -} \ No newline at end of file