From 33131e9878c48c4e728e78596c24f69b1b466489 Mon Sep 17 00:00:00 2001 From: Norbert Hartl Date: Thu, 10 Sep 2026 14:02:55 +0200 Subject: [PATCH] Cancelling a finished instance is nothing FBFlowInstance>>cancel on an instance that is over - done, or cancelled before - answers empty and leaves no trace. It used to mark the root scope of a finished case as well, so the instance read #cancelled instead of #done from then on. Co-Authored-By: Claude Fable 5.1 --- .../FBCancellationTest.class.st | 19 +++++++++++++++++++ source/FluxBase-Core/FBFlowInstance.class.st | 6 ++++-- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/source/FluxBase-Core-Tests/FBCancellationTest.class.st b/source/FluxBase-Core-Tests/FBCancellationTest.class.st index f2ceb29..86d5515 100644 --- a/source/FluxBase-Core-Tests/FBCancellationTest.class.st +++ b/source/FluxBase-Core-Tests/FBCancellationTest.class.st @@ -983,3 +983,22 @@ FBCancellationTest >> testCancellingTheRemainingIterationsTakesAWaitingSubFlowWi self assert: seen asArray equals: { #( nil nil ) }. self assertConsistent: instance ] + +{ #category : 'tests' } +FBCancellationTest >> testCancellingAFinishedInstanceIsNothing [ + "a finished case is not cancelled after the fact: no trace, and it stays #done" + | flow instance before | + flow := self flowSplittingIntoTwoEnds. + instance := flow newInstance. + instance executeWith: 1. + self assert: instance state equals: #done. + self assert: instance hasEnded. + before := instance events size. + + self assert: (instance cancel) isEmpty. + + self assert: instance state equals: #done. + self deny: instance isCancelled. + self assert: instance events size equals: before. + self assertConsistent: instance +] diff --git a/source/FluxBase-Core/FBFlowInstance.class.st b/source/FluxBase-Core/FBFlowInstance.class.st index 0bacee7..2291cff 100644 --- a/source/FluxBase-Core/FBFlowInstance.class.st +++ b/source/FluxBase-Core/FBFlowInstance.class.st @@ -1074,9 +1074,11 @@ FBFlowInstance >> cancel [ waits in me, and end - regardless of steps that failed before, an abort is a decision. I read #cancelled from then on; a late signal or a second cancel is silent. An instance that has not run yet gets its root scope for this - it has - been cancelled before it started, and says so" + been cancelled before it started, and says so. An instance that is over already - + done, or cancelled before - is left as it is: cancelling a finished case is + nothing, and it must not turn a #done into a #cancelled after the fact" - self isCancelled ifTrue: [ ^ #() ]. + self hasEnded ifTrue: [ ^ #() ]. self ensureScope. ^ [ self cancelScope: scope ] ensure: [ (executor isNil or: [ self atEnd ]) ifTrue: [ self ended ] ]