Skip to content

Commit

Permalink
Added Tracer.hasQueuedTraceEvents (#49)
Browse files Browse the repository at this point in the history
* Add waitForEmptyTraceEventsQueue

* Simplified approach to just exposing a boolean

* Incremented version

* Removed unused imports

Co-authored-by: tvkanters <[email protected]>
  • Loading branch information
TimonKanters-TomTom and tvkanters authored Aug 30, 2022
1 parent 5be0cfd commit 958fd72
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 11 deletions.
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,10 @@ Contributors: Timon Kanters, Jeroen Erik Jensen, Krzysztof Karczewski

## Release notes

### 1.8.0

* Added `Tracer.hasQueuedTraceEvents: Boolean` to support testing that certain trace events were _not_ sent.

### 1.7.2

* Fixed documentation on logging sync instead of async by default. No functional change.
Expand Down
2 changes: 1 addition & 1 deletion extensions/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.tomtom.kotlin</groupId>
<artifactId>kotlin-tools</artifactId>
<version>1.7.2</version>
<version>1.8.0</version>
</parent>

<artifactId>extensions</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion memoization/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.tomtom.kotlin</groupId>
<artifactId>kotlin-tools</artifactId>
<version>1.7.2</version>
<version>1.8.0</version>
</parent>

<artifactId>memoization</artifactId>
Expand Down
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

<groupId>com.tomtom.kotlin</groupId>
<artifactId>kotlin-tools</artifactId>
<version>1.7.2</version>
<version>1.8.0</version>
<packaging>pom</packaging>

<name>Kotlin Tools</name>
Expand Down Expand Up @@ -345,6 +345,12 @@
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>${maven-project-info-reports-plugin.version}</version>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-test</artifactId>
<version>${kotlinx-coroutines-core.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
8 changes: 7 additions & 1 deletion traceevents/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.tomtom.kotlin</groupId>
<artifactId>kotlin-tools</artifactId>
<version>1.7.2</version>
<version>1.8.0</version>
</parent>

<artifactId>traceevents</artifactId>
Expand Down Expand Up @@ -94,5 +94,11 @@
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.jetbrains.kotlinx</groupId>
<artifactId>kotlinx-coroutines-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
24 changes: 21 additions & 3 deletions traceevents/src/main/java/com/tomtom/kotlin/traceevents/Tracer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,14 @@
package com.tomtom.kotlin.traceevents

import com.tomtom.kotlin.traceevents.TraceLog.LogLevel
import kotlinx.coroutines.*
import kotlinx.coroutines.CoroutineName
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancelAndJoin
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.launch
import java.io.PrintWriter
import java.io.StringWriter
import java.lang.reflect.InvocationHandler
Expand Down Expand Up @@ -515,7 +521,20 @@ public class Tracer private constructor(
* Capacity definition for the channel that queues traces.
*/
private const val CHANNEL_CAPACITY = 10000
internal val traceEventChannel = Channel<TraceEvent>(CHANNEL_CAPACITY)
private val traceEventChannel = Channel<TraceEvent>(CHANNEL_CAPACITY)

/**
* Whether there are trace events in the queue to be processed.
*
* This can be valuable in tests to assert whether certain trace events were _not_ sent. When testing that a
* trace event was sent, it's typically sufficient to add a timeout on the verification. However, when testing
* that an event _wasn't_ sent, it is necessary to ensure that all queued trace events have been processed.
* Checking that this is `false` allows ensuring that.
*/
@OptIn(ExperimentalCoroutinesApi::class)
public val hasQueuedTraceEvents: Boolean
get() = !traceEventChannel.isEmpty

private val traceEventConsumers = TraceEventConsumerCollection()

/**
Expand Down Expand Up @@ -663,7 +682,6 @@ public class Tracer private constructor(
private suspend fun processTraceEvents() {
TraceLog.log(LogLevel.DEBUG, TAG, "Started trace event processor")
for (traceEvent in traceEventChannel) {

/**
* If the event processor is disabled, it simply discards all events
* from the queue, until it is enabled again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,12 @@ package com.tomtom.kotlin.traceevents

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import java.io.ByteArrayOutputStream
import java.io.PrintStream

Expand Down Expand Up @@ -72,16 +77,42 @@ internal fun setUpTracerTest() {
runBlocking {
TraceThreadLocalContext.clear()
TraceLog.setLogger()
Tracer.eventProcessorScope = CoroutineScope(Dispatchers.Unconfined)
Tracer.setTraceEventLoggingMode(Tracer.Companion.LoggingMode.SYNC)
Tracer.enableTraceEventLogging(true)
Tracer.removeAllTraceEventConsumers()
Tracer.cancelAndJoinEventProcessor()
advanceEventProcessorScopeUntilIdleAsync {
Tracer.cancelAndJoinEventProcessor()
}
Tracer.eventProcessorScope = CoroutineScope(Dispatchers.Unconfined)
Tracer.flushTraceEvents()
Tracer.resetToDefaults()
}
}

/**
* Dispatches coroutine jobs launched on the [Tracer.eventProcessorScope] once [block] is
* suspended. This is necessary to unsuspend [block] when using a [TestScope]. Because [TestScope]
* does not dispatch jobs until calling a method like [TestScope.advanceUntilIdle], any [block]
* waiting for [Tracer.eventProcessorScope] would wait indefinitely without using this method.
*
* If [Tracer.eventProcessorScope] is not a [TestScope], [block] executes as usual.
*/
@OptIn(ExperimentalCoroutinesApi::class)
internal suspend fun advanceEventProcessorScopeUntilIdleAsync(
block: suspend CoroutineScope.() -> Unit
) {
coroutineScope {
val advanceUntilIdleResult =
Tracer.eventProcessorScope
.let { it as? TestScope }
?.let { async { it.advanceUntilIdle() } }

block()

advanceUntilIdleResult?.await()
}
}

// Replacements for times and numbers.
internal const val TIME = "[TIME]"
internal const val NUMBER = "[NUMBER]"
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ import io.mockk.coVerifySequence
import io.mockk.spyk
import io.mockk.verify
import io.mockk.verifySequence
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import nl.jqno.equalsverifier.EqualsVerifier
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import kotlin.reflect.jvm.jvmName
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

internal class TracerTest {

Expand Down Expand Up @@ -564,6 +569,82 @@ internal class TracerTest {
assertEquals(Tracer::class.simpleName, Tracer.TAG)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `has queued trace events before processing`() = runTest {
// GIVEN
val testScope = TestScope()
Tracer.eventProcessorScope = testScope

val consumer = spyk(SpecificConsumer())
Tracer.addTraceEventConsumer(consumer)

// THEN
assertFalse(Tracer.hasQueuedTraceEvents)

// WHEN
sut.eventNoArgs()
sut.eventString("abc")

// THEN
coVerify(exactly = 0) {
consumer.eventNoArgs()
consumer.eventString("abc")
}
assertTrue(Tracer.hasQueuedTraceEvents)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `has no more queued trace events after processing`() = runTest {
// GIVEN
val testScope = TestScope()
Tracer.eventProcessorScope = testScope

val consumer = spyk(SpecificConsumer())
Tracer.addTraceEventConsumer(consumer)

sut.eventNoArgs()
sut.eventString("abc")

// WHEN
testScope.advanceUntilIdle()

// THEN
coVerifySequence {
consumer.eventNoArgs()
consumer.eventString("abc")
}
assertFalse(Tracer.hasQueuedTraceEvents)
}

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `has no more queued trace events after flushing`() = runTest {
// GIVEN
val testScope = TestScope()
Tracer.eventProcessorScope = testScope

val consumer = spyk(SpecificConsumer())
Tracer.addTraceEventConsumer(consumer)

sut.eventNoArgs()
sut.eventString("abc")

// WHEN
advanceEventProcessorScopeUntilIdleAsync {
Tracer.flushTraceEvents()
}
testScope.advanceUntilIdle()

// THEN
coVerify(exactly = 0) {
consumer.eventNoArgs()
consumer.eventString("abc")
}
assertFalse(Tracer.hasQueuedTraceEvents)
}

companion object {
val sut = Tracer.Factory.create<MyEvents>(this)
val sutMain = Tracer.Factory.create<MyEvents>(this, "the main tracer")
Expand Down
2 changes: 1 addition & 1 deletion uid/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
<parent>
<groupId>com.tomtom.kotlin</groupId>
<artifactId>kotlin-tools</artifactId>
<version>1.7.2</version>
<version>1.8.0</version>
</parent>

<artifactId>uid</artifactId>
Expand Down

0 comments on commit 958fd72

Please sign in to comment.