Skip to content

Commit

Permalink
Add public debuggingName to WorkflowAction, use it in default toString()
Browse files Browse the repository at this point in the history
  • Loading branch information
steve-the-edwards committed Nov 14, 2024
1 parent 86dab95 commit aeae4c0
Show file tree
Hide file tree
Showing 15 changed files with 61 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
package com.squareup.workflow1

import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume
import kotlin.jvm.JvmMultifileClass
Expand Down Expand Up @@ -86,7 +85,8 @@ internal suspend fun <
) {
suspendCancellableCoroutine<Unit> { continuation ->
val resumingAction = object : WorkflowAction<PropsT, StateT, OutputT>() {
override fun toString(): String = "sendAndAwaitApplication($action)"
override fun toString(): String = "sendAndAwaitApplication(${action})"

override fun Updater.apply() {
// Don't execute anything if the caller was cancelled while we were in the queue.
if (!continuation.isActive) return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1007,6 +1007,8 @@ public fun <PropsT, StateT, OutputT, RenderingT>
name: () -> String,
update: WorkflowAction<PropsT, StateT, OutputT>.Updater.() -> Unit
): WorkflowAction<PropsT, StateT, OutputT> = object : WorkflowAction<PropsT, StateT, OutputT>() {
override val debuggingName: String
get() = name()

override fun Updater.apply() = update.invoke(this)
override fun toString(): String = "action(${name()})-${this@action}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ public fun <PropsT, OutputT, RenderingT>
name: () -> String,
update: WorkflowAction<PropsT, *, OutputT>.Updater.() -> Unit
): WorkflowAction<PropsT, Nothing, OutputT> = object : WorkflowAction<PropsT, Nothing, OutputT>() {
override val debuggingName: String
get() = name()

override fun Updater.apply() = update.invoke(this)
override fun toString(): String = "action(${name()})-${this@action}"
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ private class EmitWorkerOutputAction<P, S, O>(
private val renderKey: String,
private val output: O,
) : WorkflowAction<P, S, O>() {
override fun toString(): String =
WorkflowIdentifierTypeNamer.uniqueName(EmitWorkerOutputAction::class) +
override val debuggingName: String
get() = CommonKClassTypeNamer.uniqueName(EmitWorkerOutputAction::class) +
"(worker=$worker, key=$renderKey)"

override fun Updater.apply() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ import kotlin.jvm.JvmOverloads
*/
public abstract class WorkflowAction<in PropsT, StateT, out OutputT> {

// Debugging name, will be returned by `toString()`
public open val debuggingName: String = CommonKClassTypeNamer.uniqueName(this::class)

/**
* The context for calls to [WorkflowAction.apply]. Allows the action to read and change the
* [state], and to emit an [output][setOutput] value.
Expand Down Expand Up @@ -60,6 +63,8 @@ public abstract class WorkflowAction<in PropsT, StateT, out OutputT> {
*/
public abstract fun Updater.apply()

public override fun toString(): String = "action(${debuggingName})-@${hashCode()}"

public companion object {
/**
* Returns a [WorkflowAction] that does nothing: no output will be emitted, and
Expand All @@ -72,7 +77,7 @@ public abstract class WorkflowAction<in PropsT, StateT, out OutputT> {
NO_ACTION as WorkflowAction<Any?, StateT, OutputT>

private val NO_ACTION = object : WorkflowAction<Any?, Any?, Any?>() {
override fun toString(): String = "WorkflowAction.noAction()"
override val debuggingName: String = "noAction()"

override fun Updater.apply() {
// Noop
Expand Down Expand Up @@ -124,9 +129,10 @@ public fun <PropsT, StateT, OutputT> action(
name: () -> String,
apply: WorkflowAction<PropsT, StateT, OutputT>.Updater.() -> Unit
): WorkflowAction<PropsT, StateT, OutputT> = object : WorkflowAction<PropsT, StateT, OutputT>() {
override fun Updater.apply() = apply.invoke(this)
override val debuggingName: String
get() = name()

override fun toString(): String = "WorkflowAction(${name()})@${hashCode()}"
override fun Updater.apply() = apply.invoke(this)
}

/** Applies this [WorkflowAction] to [state]. */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public sealed class WorkflowIdentifierType {
val kClass: KClass<*>? = null,
) : WorkflowIdentifierType() {
public constructor(kClass: KClass<*>) : this(
WorkflowIdentifierTypeNamer.uniqueName(kClass),
CommonKClassTypeNamer.uniqueName(kClass),
kClass
)
}
Expand All @@ -46,6 +46,6 @@ public sealed class WorkflowIdentifierType {
}
}

internal expect object WorkflowIdentifierTypeNamer {
internal expect object CommonKClassTypeNamer {
public fun uniqueName(kClass: KClass<*>): String
}
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ internal class WorkflowIdentifierTest {
) : Workflow<Nothing, Nothing, Nothing>, ImpostorWorkflow {
override val realIdentifier: WorkflowIdentifier = proxied.identifier
override fun describeRealIdentifier(): String =
"TestImpostor1(${WorkflowIdentifierTypeNamer.uniqueName(proxied::class)})"
"TestImpostor1(${CommonKClassTypeNamer.uniqueName(proxied::class)})"
override fun asStatefulWorkflow(): StatefulWorkflow<Nothing, *, Nothing, Nothing> =
throw NotImplementedError()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.squareup.workflow1

import kotlin.reflect.KClass

internal actual object WorkflowIdentifierTypeNamer {
internal actual object CommonKClassTypeNamer {
public actual fun uniqueName(kClass: KClass<*>): String {
return kClass.qualifiedName ?: kClass.toString()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.squareup.workflow1

import kotlin.reflect.KClass

internal actual object WorkflowIdentifierTypeNamer {
internal actual object CommonKClassTypeNamer {
// Stores mappings between KClass instances and their assigned names.
val mappings = mutableMapOf<KClass<*>, String>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.squareup.workflow1

import kotlin.reflect.KClass

internal actual object WorkflowIdentifierTypeNamer {
internal actual object CommonKClassTypeNamer {
public actual fun uniqueName(kClass: KClass<*>): String {
return kClass.qualifiedName ?: kClass.toString()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,12 +124,12 @@ internal class RealRenderContextTest {
@Test fun send_allows_multiple_sends() {
val context = createdPoisonedContext()
val firstAction = object : WorkflowAction<String, String, String>() {
override val debuggingName: String = "firstAction"
override fun Updater.apply() = Unit
override fun toString(): String = "firstAction"
}
val secondAction = object : WorkflowAction<String, String, String>() {
override val debuggingName: String = "secondAction"
override fun Updater.apply() = Unit
override fun toString(): String = "secondAction"
}
// Enable sink sends.
context.freeze()
Expand All @@ -143,8 +143,8 @@ internal class RealRenderContextTest {
@Test fun send_throws_before_render_returns() {
val context = createdPoisonedContext()
val action = object : WorkflowAction<String, String, String>() {
override val debuggingName: String = "action"
override fun Updater.apply() = Unit
override fun toString(): String = "action"
}

val error = assertFailsWith<UnsupportedOperationException> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ internal class WorkflowNodeTest {
@Test fun send_fails_before_render_pass_completed() {
class TestAction : WorkflowAction<Unit, Nothing, Nothing>() {
override fun Updater.apply() = fail("Expected sink send to fail.")
override fun toString(): String = "TestAction()"
override val debuggingName: String = "TestAction()"
}

val workflow = Workflow.stateless {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ internal class RealRenderTesterTest {
private interface OutputWhateverChild : Workflow<Unit, Unit, Unit>
private interface OutputNothingChild : Workflow<Unit, Nothing, Unit>

// [WorkflowAction::toString] includes "@-${hashCode()}", so strip it.
private fun String.removeActionHashCodes(): String = replace(Regex("-@([0-9]*)"), "")

@Test fun `renderChild throws when already expecting workflow output`() {
val child1 = Workflow.stateless<Unit, Unit, Unit> {}
val child2 = Workflow.stateless<Unit, Unit, Unit> {}
Expand All @@ -64,8 +67,8 @@ internal class RealRenderTesterTest {
}
assertEquals(
"Expected only one output to be expected: child ${child2.identifier} " +
"expected to emit kotlin.Unit but WorkflowAction.noAction() was already processed.",
failure.message
"expected to emit kotlin.Unit but action(noAction()) was already processed.",
failure.message?.removeActionHashCodes()
)
}

Expand All @@ -87,8 +90,8 @@ internal class RealRenderTesterTest {
assertEquals(
"Expected only one output to be expected: " +
"child ${typeOf<Worker<Unit>>()} expected to emit " +
"kotlin.Unit but WorkflowAction.noAction() was already processed.",
failure.message
"kotlin.Unit but action(noAction()) was already processed.",
failure.message?.removeActionHashCodes()
)
}

Expand Down Expand Up @@ -154,9 +157,9 @@ internal class RealRenderTesterTest {
}

@Test fun `sending to sink throws when called multiple times`() {
class TestAction(private val name: String) : WorkflowAction<Unit, Unit, Nothing>() {
class TestAction(name: String) : WorkflowAction<Unit, Unit, Nothing>() {
override fun Updater.apply() {}
override fun toString(): String = "TestAction($name)"
override val debuggingName: String = "TestAction($name)"
}

val workflow = Workflow.stateful<Unit, Nothing, Sink<TestAction>>(
Expand Down Expand Up @@ -185,7 +188,7 @@ internal class RealRenderTesterTest {
@Test fun `sending to sink throws when child output expected`() {
class TestAction : WorkflowAction<Unit, Unit, Nothing>() {
override fun Updater.apply() {}
override fun toString(): String = "TestAction"
override val debuggingName: String = "TestAction"
}

val workflow = Workflow.stateful<Unit, Nothing, Sink<TestAction>>(
Expand All @@ -205,9 +208,9 @@ internal class RealRenderTesterTest {
}
assertEquals(
"Tried to send action to sink after another action was already processed:\n" +
" processed action=WorkflowAction.noAction()\n" +
" attempted action=TestAction",
error.message
" processed action=action(noAction())\n" +
" attempted action=action(TestAction)",
error.message?.removeActionHashCodes()
)
}
}
Expand Down Expand Up @@ -964,9 +967,9 @@ internal class RealRenderTesterTest {
assertEquals("bad props: wrong props", error.message)
}

private class TestAction(val name: String) : WorkflowAction<Unit, Nothing, Nothing>() {
private class TestAction(name: String) : WorkflowAction<Unit, Nothing, Nothing>() {
override fun Updater.apply() {}
override fun toString(): String = "TestAction($name)"
override val debuggingName: String = name
}

@Test fun `verifyAction failure fails test`() {
Expand Down Expand Up @@ -997,7 +1000,7 @@ internal class RealRenderTesterTest {

testResult.verifyAction {
assertTrue(it is TestAction)
assertEquals("output", it.name)
assertEquals("output", it.debuggingName)
}
}

Expand All @@ -1012,7 +1015,7 @@ internal class RealRenderTesterTest {

testResult.verifyAction {
assertTrue(it is TestAction)
assertEquals("output", it.name)
assertEquals("output", it.debuggingName)
}
}

Expand All @@ -1027,7 +1030,7 @@ internal class RealRenderTesterTest {

testResult.verifyAction {
assertTrue(it is TestAction)
assertEquals("event", it.name)
assertEquals("event", it.debuggingName)
}
}

Expand Down Expand Up @@ -1087,6 +1090,7 @@ internal class RealRenderTesterTest {

@Test fun `testNextRender could daisy-chain consecutive renderings with verifyAction`() {
data class TestAction(val add: Int) : WorkflowAction<Unit, Int, Int>() {
override val debuggingName: String get() = "add:$add"
override fun Updater.apply() {
setOutput(state)
state += add
Expand Down Expand Up @@ -1123,6 +1127,8 @@ internal class RealRenderTesterTest {

@Test fun `testNextRender could daisy-chain consecutive renderings with verifyActionResult`() {
data class TestAction(val add: Int) : WorkflowAction<Unit, Int, Int>() {
override val debuggingName: String get() = "add:$add"

override fun Updater.apply() {
setOutput(state)
state += add
Expand Down Expand Up @@ -1162,6 +1168,8 @@ internal class RealRenderTesterTest {

@Test fun `testNextRenderWithProps respects new props`() {
data class TestAction(val add: Int) : WorkflowAction<Int, Int, Int>() {
override val debuggingName: String get() = "add:$add"

override fun Updater.apply() {
setOutput(state)
state += props * add
Expand Down Expand Up @@ -1201,6 +1209,8 @@ internal class RealRenderTesterTest {

@Test fun `testNextRenderWithProps uses onPropsChanged`() {
data class TestAction(val add: Int) : WorkflowAction<Int, Int, Int>() {
override val debuggingName: String get() = "add:$add"

override fun Updater.apply() {
setOutput(state)
state += props * add
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import com.squareup.workflow1.renderWorkflowIn
import com.squareup.workflow1.runningWorker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers.Unconfined
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.Channel.Factory.UNLIMITED
Expand All @@ -30,7 +29,6 @@ import org.mockito.kotlin.mock
import kotlin.test.Test
import kotlin.test.assertEquals

@OptIn(ExperimentalCoroutinesApi::class)
internal class TracingWorkflowInterceptorTest {

private lateinit var onGcDetected: () -> Unit
Expand Down Expand Up @@ -85,7 +83,8 @@ internal class TracingWorkflowInterceptorTest {
.getResourceAsStream("expected_trace_file.txt")
.source()
.buffer()
assertEquals(expected.readUtf8(), buffer.readUtf8())
.readUtf8()
assertEquals(expected, buffer.readUtf8().removeActionHashCodes())
}

private inner class TestWorkflow : StatefulWorkflow<Int, String, String, String>() {
Expand Down Expand Up @@ -141,6 +140,9 @@ internal class TracingWorkflowInterceptorTest {
}
}

// [WorkflowAction::toString] includes "@-${hashCode()}", so strip it.
private fun String.removeActionHashCodes(): String = replace(Regex("-@([0-9]*)"), "")

private object ZeroTimeMark : TimeMark {
override val elapsedNow: Long = 0L
}
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@
{"name":"used/free memory","ph":"C","ts":0,"pid":0,"tid":0,"args":{"usedMemory":1,"freeMemory":42}},
{"name":"Snapshot","ph":"B","ts":0,"pid":0,"tid":0,"args":{}},
{"name":"Snapshot","ph":"E","ts":0,"pid":0,"tid":0,"args":{}},
{"name":"Sink received: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"t","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=))"}},
{"name":"WorkflowAction: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"p","args":{"action":"sendAndAwaitApplication(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=))","oldState":"0","newState":"{no change}","output":"fired!"}},
{"name":"Sink received: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"t","args":{"action":"sendAndAwaitApplication(action(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=)))"}},
{"name":"WorkflowAction: Worker<String> (2)","cat":"update","ph":"i","ts":0,"pid":0,"tid":0,"s":"p","args":{"action":"sendAndAwaitApplication(action(com.squareup.workflow1.EmitWorkerOutputAction(worker=TypedWorker(java.lang.String (Kotlin reflection is not available)), key=)))","oldState":"0","newState":"{no change}","output":"fired!"}},
{"name":"Worker<String> (2)","ph":"O","ts":0,"pid":0,"tid":0,"id":"2","args":{"snapshot":"0"}},
{"name":"Render Pass","cat":"rendering","ph":"B","ts":0,"pid":0,"tid":0,"args":{"props":"3"}},
{"name":"used/free memory","ph":"C","ts":0,"pid":0,"tid":0,"args":{"usedMemory":1,"freeMemory":42}},
Expand Down

0 comments on commit aeae4c0

Please sign in to comment.