diff --git a/documentation/components/bridges/phpunit-telemetry-bridge.md b/documentation/components/bridges/phpunit-telemetry-bridge.md
index e70c00909..f1a9060d8 100644
--- a/documentation/components/bridges/phpunit-telemetry-bridge.md
+++ b/documentation/components/bridges/phpunit-telemetry-bridge.md
@@ -30,6 +30,7 @@ Add the extension to your `phpunit.xml.dist`:
+
```
@@ -48,6 +49,7 @@ Add the extension to your `phpunit.xml.dist`:
| `emit_metrics` | `FLOW_PHPUNIT_OTEL_EMIT_METRICS` | `true` | Enable/disable metric emission |
| `emit_test_spans` | `FLOW_PHPUNIT_OTEL_EMIT_TEST_SPANS` | `true` | Create individual spans for each test |
| `emit_test_case_spans` | `FLOW_PHPUNIT_OTEL_EMIT_TEST_CASE_SPANS` | `true` | Create spans for test case classes |
+| `memory_real_usage` | `FLOW_PHPUNIT_OTEL_MEMORY_REAL_USAGE` | `false` | Report total system-allocated memory (`real_usage=true`) instead of emalloc usage for memory metrics/attributes |
| `batch_size` | `FLOW_PHPUNIT_OTEL_BATCH_SIZE` | `512` | Items per batch for span/metric/log batching processors |
| `shutdown_timeout_ms` | `FLOW_PHPUNIT_OTEL_SHUTDOWN_TIMEOUT_MS` | `5000` | Wall-clock budget in ms for draining pending requests at shutdown (curl/grpc) |
| `error_handler` | `FLOW_PHPUNIT_OTEL_ERROR_HANDLER` | `error_log` | How telemetry errors are surfaced (see [Error Handlers](#error-handlers)) |
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
index dce07f20b..bd9695f71 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Configuration.php
@@ -136,6 +136,7 @@ public function __construct(
public bool $emitTestCaseSpans,
public int $batchSize,
public ErrorLogHandlerConfig|NullErrorHandlerConfig|StreamErrorHandlerConfig|SyslogErrorHandlerConfig|UdpSyslogErrorHandlerConfig $errorHandler,
+ public bool $memoryRealUsage = false,
) {}
public static function fromParameters(ParameterCollection $parameters): self
@@ -165,6 +166,7 @@ public static function fromParameters(ParameterCollection $parameters): self
emitTestCaseSpans: self::resolveBool($parameters, 'emit_test_case_spans', true),
batchSize: self::resolveInt($parameters, 'batch_size', self::DEFAULT_BATCH_SIZE),
errorHandler: self::resolveErrorHandler($parameters),
+ memoryRealUsage: self::resolveBool($parameters, 'memory_real_usage', false),
);
}
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestFinishedSubscriber.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestFinishedSubscriber.php
index 5e467c008..d10021cec 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestFinishedSubscriber.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestFinishedSubscriber.php
@@ -6,6 +6,7 @@
use Flow\Bridge\PHPUnit\Telemetry\Configuration;
use Flow\Bridge\PHPUnit\Telemetry\SpanStack;
+use Flow\Bridge\PHPUnit\Telemetry\TestMemoryRegistry;
use Flow\Bridge\PHPUnit\Telemetry\TestStatusRegistry;
use Flow\Telemetry\PackageVersion;
use Flow\Telemetry\Telemetry;
@@ -14,6 +15,9 @@
use PHPUnit\Event\Test\FinishedSubscriber;
use Throwable;
+use function memory_get_peak_usage;
+use function memory_get_usage;
+
final readonly class TestFinishedSubscriber implements FinishedSubscriber
{
public function __construct(
@@ -21,6 +25,7 @@ public function __construct(
private SpanStack $spanStack,
private Configuration $config,
private TestStatusRegistry $statusRegistry,
+ private TestMemoryRegistry $memoryRegistry,
) {}
public function notify(Finished $event): void
@@ -29,11 +34,25 @@ public function notify(Finished $event): void
$testId = $event->test()->id();
$status = $this->statusRegistry->getStatus($testId);
+ $startBytes = $this->memoryRegistry->getStart($testId);
+ $peakBytes = memory_get_peak_usage($this->config->memoryRealUsage);
+ $deltaBytes = $startBytes !== null ? memory_get_usage($this->config->memoryRealUsage) - $startBytes : null;
+ $this->memoryRegistry->clear($testId);
+
if (!$this->config->emitTestSpans) {
if ($this->config->emitMetrics) {
$meter = $this->telemetry->meter('phpunit', PackageVersion::get('phpunit/phpunit'));
$meter->createCounter('phpunit.test.count')->add(1, ['test.status' => $status]);
+ $meter->createHistogram('phpunit.test.memory.peak', 'bytes')->record($peakBytes, [
+ 'test.status' => $status,
+ ]);
+
+ if ($deltaBytes !== null) {
+ $meter->createHistogram('phpunit.test.memory.delta', 'bytes')->record($deltaBytes, [
+ 'test.status' => $status,
+ ]);
+ }
}
$this->statusRegistry->clear($testId);
@@ -63,18 +82,38 @@ public function notify(Finished $event): void
$span->setAttribute('exception.message', $errorMessage);
}
+ $span->setAttribute('test.memory.peak_bytes', $peakBytes);
+
+ if ($deltaBytes !== null) {
+ $span->setAttribute('test.memory.delta_bytes', $deltaBytes);
+ }
+
if ($status === 'passed') {
$span->setStatus(SpanStatus::ok());
} else {
$span->setStatus(SpanStatus::error($errorMessage ?? $status));
}
- if ($this->config->emitMetrics && $duration !== null) {
+ if ($this->config->emitMetrics) {
$meter = $this->telemetry->meter('phpunit', PackageVersion::get('phpunit/phpunit'));
- $meter->createHistogram('phpunit.test.duration', 'ms')->record($duration, ['test.status' => $status]);
+ if ($duration !== null) {
+ $meter->createHistogram('phpunit.test.duration', 'ms')->record($duration, [
+ 'test.status' => $status,
+ ]);
- $meter->createCounter('phpunit.test.count')->add(1, ['test.status' => $status]);
+ $meter->createCounter('phpunit.test.count')->add(1, ['test.status' => $status]);
+ }
+
+ $meter->createHistogram('phpunit.test.memory.peak', 'bytes')->record($peakBytes, [
+ 'test.status' => $status,
+ ]);
+
+ if ($deltaBytes !== null) {
+ $meter->createHistogram('phpunit.test.memory.delta', 'bytes')->record($deltaBytes, [
+ 'test.status' => $status,
+ ]);
+ }
}
$tracer->complete($span);
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestPreparationStartedSubscriber.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestPreparationStartedSubscriber.php
index c877a55cb..35cd9ce49 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestPreparationStartedSubscriber.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/Subscriber/TestPreparationStartedSubscriber.php
@@ -6,23 +6,33 @@
use Flow\Bridge\PHPUnit\Telemetry\Configuration;
use Flow\Bridge\PHPUnit\Telemetry\SpanStack;
+use Flow\Bridge\PHPUnit\Telemetry\TestMemoryRegistry;
use Flow\Telemetry\PackageVersion;
use Flow\Telemetry\Telemetry;
use PHPUnit\Event\Test\PreparationStarted;
use PHPUnit\Event\Test\PreparationStartedSubscriber;
use Throwable;
+use function memory_get_usage;
+use function memory_reset_peak_usage;
+
final readonly class TestPreparationStartedSubscriber implements PreparationStartedSubscriber
{
public function __construct(
private Telemetry $telemetry,
private SpanStack $spanStack,
private Configuration $config,
+ private TestMemoryRegistry $memoryRegistry,
) {}
public function notify(PreparationStarted $event): void
{
try {
+ if ($this->config->emitTestSpans || $this->config->emitMetrics) {
+ memory_reset_peak_usage();
+ $this->memoryRegistry->setStart($event->test()->id(), memory_get_usage($this->config->memoryRealUsage));
+ }
+
if (!$this->config->emitTestSpans) {
return;
}
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TelemetryExtension.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TelemetryExtension.php
index bc4960811..503bee987 100644
--- a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TelemetryExtension.php
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TelemetryExtension.php
@@ -31,17 +31,18 @@ public function bootstrap(
$telemetry = TelemetryFactory::create($config);
$spanStack = new SpanStack();
$statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
$facade->registerSubscribers(
new TestSuiteStartedSubscriber($telemetry, $spanStack, $config),
new TestSuiteFinishedSubscriber($telemetry, $spanStack, $config),
- new TestPreparationStartedSubscriber($telemetry, $spanStack, $config),
+ new TestPreparationStartedSubscriber($telemetry, $spanStack, $config, $memoryRegistry),
new TestPassedSubscriber($statusRegistry),
new TestFailedSubscriber($statusRegistry),
new TestErroredSubscriber($statusRegistry),
new TestSkippedSubscriber($statusRegistry),
new TestMarkedIncompleteSubscriber($statusRegistry),
- new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry),
+ new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry),
);
} catch (Throwable) {
// Silent failure - telemetry must never break tests
diff --git a/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TestMemoryRegistry.php b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TestMemoryRegistry.php
new file mode 100644
index 000000000..c77a1061e
--- /dev/null
+++ b/src/bridge/phpunit/telemetry/src/Flow/Bridge/PHPUnit/Telemetry/TestMemoryRegistry.php
@@ -0,0 +1,28 @@
+
+ */
+ private array $startBytes = [];
+
+ public function clear(string $testId): void
+ {
+ unset($this->startBytes[$testId]);
+ }
+
+ public function getStart(string $testId): ?int
+ {
+ return $this->startBytes[$testId] ?? null;
+ }
+
+ public function setStart(string $testId, int $bytes): void
+ {
+ $this->startBytes[$testId] = $bytes;
+ }
+}
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/ConfigurationMother.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/ConfigurationMother.php
index 715f4076a..625c23a85 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/ConfigurationMother.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/ConfigurationMother.php
@@ -139,4 +139,19 @@ public static function withDisabledTraces(): Configuration
errorHandler: self::defaultErrorHandler(),
);
}
+
+ public static function withRealMemoryUsage(): Configuration
+ {
+ return new Configuration(
+ serviceName: 'phpunit',
+ transport: self::defaultTransport(),
+ emitTraces: true,
+ emitMetrics: true,
+ emitTestSpans: true,
+ emitTestCaseSpans: true,
+ batchSize: Configuration::DEFAULT_BATCH_SIZE,
+ errorHandler: self::defaultErrorHandler(),
+ memoryRealUsage: true,
+ );
+ }
}
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/TelemetryMother.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/TelemetryMother.php
index 2eb69f393..0e1a9b6e2 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/TelemetryMother.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Mother/TelemetryMother.php
@@ -5,6 +5,7 @@
namespace Flow\Bridge\PHPUnit\Telemetry\Tests\Mother;
use Flow\Telemetry\Provider\Clock\SystemClock;
+use Flow\Telemetry\Provider\Memory\MemoryMetricProcessor;
use Flow\Telemetry\Provider\Memory\MemorySpanProcessor;
use Flow\Telemetry\Telemetry;
use Flow\Telemetry\Tests\Mother\ResourceMother;
@@ -20,19 +21,26 @@
final class TelemetryMother
{
- public static function create(?MemorySpanProcessor $spanProcessor = null): Telemetry
- {
+ public static function create(
+ ?MemorySpanProcessor $spanProcessor = null,
+ ?MemoryMetricProcessor $metricProcessor = null,
+ ): Telemetry {
$clock = new SystemClock();
$contextStorage = memory_context_storage();
return new Telemetry(
ResourceMother::default(),
tracer_provider($spanProcessor ?? memory_span_processor(void_exporter()), $clock, $contextStorage),
- meter_provider(memory_metric_processor(void_exporter()), $clock),
+ meter_provider($metricProcessor ?? memory_metric_processor(void_exporter()), $clock),
logger_provider(memory_log_processor(void_exporter()), $clock, $contextStorage),
);
}
+ public static function withMetricProcessor(MemoryMetricProcessor $metricProcessor): Telemetry
+ {
+ return self::create(metricProcessor: $metricProcessor);
+ }
+
public static function withSpanProcessor(MemorySpanProcessor $spanProcessor): Telemetry
{
return self::create($spanProcessor);
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/ConfigurationTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/ConfigurationTest.php
index 0b9961d84..331694af2 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/ConfigurationTest.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/ConfigurationTest.php
@@ -252,6 +252,7 @@ public function test_default_configuration_uses_curl_transport_with_localhost_en
static::assertTrue($config->emitMetrics);
static::assertTrue($config->emitTestSpans);
static::assertTrue($config->emitTestCaseSpans);
+ static::assertFalse($config->memoryRealUsage);
static::assertInstanceOf(CurlTransportConfig::class, $config->transport);
static::assertSame('http://localhost:4318', $config->transport->endpoint);
@@ -300,6 +301,15 @@ public function test_emit_flags_can_be_disabled(): void
static::assertFalse($config->emitTestCaseSpans);
}
+ public function test_memory_real_usage_can_be_enabled(): void
+ {
+ $config = Configuration::fromParameters(ParameterCollection::fromArray([
+ 'memory_real_usage' => 'true',
+ ]));
+
+ static::assertTrue($config->memoryRealUsage);
+ }
+
public function test_empty_env_superglobal_treated_as_unset(): void
{
$_ENV['FLOW_PHPUNIT_OTEL_ENDPOINT'] = '';
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
index fd3ca3bf5..85a076456 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestFinishedSubscriberTest.php
@@ -6,6 +6,7 @@
use Flow\Bridge\PHPUnit\Telemetry\SpanStack;
use Flow\Bridge\PHPUnit\Telemetry\Subscriber\TestFinishedSubscriber;
+use Flow\Bridge\PHPUnit\Telemetry\TestMemoryRegistry;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\ConfigurationMother;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\TelemetryMother;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\TestEventMother;
@@ -13,6 +14,7 @@
use Flow\Telemetry\Tests\Mother\SpanMother;
use PHPUnit\Framework\TestCase;
+use function Flow\Telemetry\DSL\memory_metric_processor;
use function Flow\Telemetry\DSL\memory_span_processor;
use function Flow\Telemetry\DSL\void_exporter;
@@ -25,16 +27,39 @@ public function test_clears_status_registry_when_emit_test_spans_is_disabled():
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
$statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
$event = TestEventMother::finished();
$statusRegistry->setStatus($event->test()->id(), 'failed', 'some error');
- $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry);
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
$subscriber->notify($event);
static::assertNull($statusRegistry->getMessage($event->test()->id()));
}
+ public function test_clears_memory_registry_after_finish(): void
+ {
+ $spanProcessor = memory_span_processor(void_exporter());
+ $telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::default();
+ $statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $testSpan = SpanMother::create('test-span');
+ $spanStack->push($testSpan);
+
+ $event = TestEventMother::finished();
+ $statusRegistry->setStatus($event->test()->id(), 'passed');
+ $memoryRegistry->setStart($event->test()->id(), 1024);
+
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
+ $subscriber->notify($event);
+
+ static::assertNull($memoryRegistry->getStart($event->test()->id()));
+ }
+
public function test_does_not_pop_span_when_emit_test_spans_is_disabled(): void
{
$spanProcessor = memory_span_processor(void_exporter());
@@ -42,6 +67,7 @@ public function test_does_not_pop_span_when_emit_test_spans_is_disabled(): void
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
$statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
$suiteSpan = SpanMother::create('suite-span');
$spanStack->push($suiteSpan);
@@ -49,7 +75,7 @@ public function test_does_not_pop_span_when_emit_test_spans_is_disabled(): void
$event = TestEventMother::finished();
$statusRegistry->setStatus($event->test()->id(), 'passed');
- $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry);
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
$subscriber->notify($event);
static::assertFalse($spanStack->isEmpty());
@@ -63,6 +89,7 @@ public function test_pops_span_when_emit_test_spans_is_enabled(): void
$spanStack = new SpanStack();
$config = ConfigurationMother::default();
$statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
$testSpan = SpanMother::create('test-span');
$spanStack->push($testSpan);
@@ -70,10 +97,100 @@ public function test_pops_span_when_emit_test_spans_is_enabled(): void
$event = TestEventMother::finished();
$statusRegistry->setStatus($event->test()->id(), 'passed');
- $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry);
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
$subscriber->notify($event);
static::assertTrue($spanStack->isEmpty());
static::assertCount(1, $spanProcessor->endedSpans());
}
+
+ public function test_records_memory_metrics_when_metrics_enabled(): void
+ {
+ $metricProcessor = memory_metric_processor(void_exporter());
+ $telemetry = TelemetryMother::withMetricProcessor($metricProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::default();
+ $statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $spanStack->push(SpanMother::create('test-span'));
+
+ $event = TestEventMother::finished();
+ $statusRegistry->setStatus($event->test()->id(), 'passed');
+ $memoryRegistry->setStart($event->test()->id(), 1024);
+
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
+ $subscriber->notify($event);
+ $telemetry->flush();
+
+ static::assertNotEmpty($metricProcessor->metricsWithName('phpunit.test.memory.peak'));
+ static::assertNotEmpty($metricProcessor->metricsWithName('phpunit.test.memory.delta'));
+ }
+
+ public function test_records_memory_delta_metric_when_only_metrics_enabled(): void
+ {
+ $metricProcessor = memory_metric_processor(void_exporter());
+ $telemetry = TelemetryMother::withMetricProcessor($metricProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::withDisabledTestSpans();
+ $statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $event = TestEventMother::finished();
+ $statusRegistry->setStatus($event->test()->id(), 'passed');
+ $memoryRegistry->setStart($event->test()->id(), 1024);
+
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
+ $subscriber->notify($event);
+ $telemetry->flush();
+
+ static::assertNotEmpty($metricProcessor->metricsWithName('phpunit.test.memory.peak'));
+ static::assertNotEmpty($metricProcessor->metricsWithName('phpunit.test.memory.delta'));
+ }
+
+ public function test_sets_error_status_and_message_on_span_for_failed_test(): void
+ {
+ $spanProcessor = memory_span_processor(void_exporter());
+ $telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::default();
+ $statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $spanStack->push(SpanMother::create('test-span'));
+
+ $event = TestEventMother::finished();
+ $statusRegistry->setStatus($event->test()->id(), 'failed', 'some error');
+
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
+ $subscriber->notify($event);
+
+ $span = $spanProcessor->endedSpans()[0];
+ static::assertTrue($span->status()?->isError());
+ static::assertArrayHasKey('exception.message', $span->attributes());
+ static::assertSame('some error', $span->attributes()['exception.message']);
+ }
+
+ public function test_sets_memory_attributes_on_span(): void
+ {
+ $spanProcessor = memory_span_processor(void_exporter());
+ $telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::default();
+ $statusRegistry = new TestStatusRegistry();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $spanStack->push(SpanMother::create('test-span'));
+
+ $event = TestEventMother::finished();
+ $statusRegistry->setStatus($event->test()->id(), 'passed');
+ $memoryRegistry->setStart($event->test()->id(), 1024);
+
+ $subscriber = new TestFinishedSubscriber($telemetry, $spanStack, $config, $statusRegistry, $memoryRegistry);
+ $subscriber->notify($event);
+
+ $attributes = $spanProcessor->endedSpans()[0]->attributes();
+ static::assertArrayHasKey('test.memory.peak_bytes', $attributes);
+ static::assertArrayHasKey('test.memory.delta_bytes', $attributes);
+ }
}
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
index 970769e9a..f9781e728 100644
--- a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/Subscriber/TestPreparationStartedSubscriberTest.php
@@ -6,6 +6,7 @@
use Flow\Bridge\PHPUnit\Telemetry\SpanStack;
use Flow\Bridge\PHPUnit\Telemetry\Subscriber\TestPreparationStartedSubscriber;
+use Flow\Bridge\PHPUnit\Telemetry\TestMemoryRegistry;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\ConfigurationMother;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\TelemetryMother;
use Flow\Bridge\PHPUnit\Telemetry\Tests\Mother\TestEventMother;
@@ -22,8 +23,9 @@ public function test_creates_span_when_emit_test_spans_is_enabled(): void
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::default();
+ $memoryRegistry = new TestMemoryRegistry();
- $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config);
+ $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config, $memoryRegistry);
$subscriber->notify(TestEventMother::preparationStarted());
static::assertFalse($spanStack->isEmpty());
@@ -36,11 +38,45 @@ public function test_does_not_create_span_when_emit_test_spans_is_disabled(): vo
$telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
$spanStack = new SpanStack();
$config = ConfigurationMother::withDisabledTestSpans();
+ $memoryRegistry = new TestMemoryRegistry();
- $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config);
+ $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config, $memoryRegistry);
$subscriber->notify(TestEventMother::preparationStarted());
static::assertTrue($spanStack->isEmpty());
static::assertCount(0, $spanProcessor->startedSpans());
}
+
+ public function test_records_memory_start_when_spans_enabled(): void
+ {
+ $spanProcessor = memory_span_processor(void_exporter());
+ $telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::default();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $event = TestEventMother::preparationStarted();
+
+ $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config, $memoryRegistry);
+ $subscriber->notify($event);
+
+ static::assertNotNull($memoryRegistry->getStart($event->test()->id()));
+ }
+
+ public function test_records_memory_start_when_only_metrics_enabled(): void
+ {
+ $spanProcessor = memory_span_processor(void_exporter());
+ $telemetry = TelemetryMother::withSpanProcessor($spanProcessor);
+ $spanStack = new SpanStack();
+ $config = ConfigurationMother::withDisabledTestSpans();
+ $memoryRegistry = new TestMemoryRegistry();
+
+ $event = TestEventMother::preparationStarted();
+
+ $subscriber = new TestPreparationStartedSubscriber($telemetry, $spanStack, $config, $memoryRegistry);
+ $subscriber->notify($event);
+
+ static::assertTrue($spanStack->isEmpty());
+ static::assertNotNull($memoryRegistry->getStart($event->test()->id()));
+ }
}
diff --git a/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TestMemoryRegistryTest.php b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TestMemoryRegistryTest.php
new file mode 100644
index 000000000..2572916e3
--- /dev/null
+++ b/src/bridge/phpunit/telemetry/tests/Flow/Bridge/PHPUnit/Telemetry/Tests/Unit/TestMemoryRegistryTest.php
@@ -0,0 +1,36 @@
+setStart('test-id', 1024);
+
+ $registry->clear('test-id');
+
+ static::assertNull($registry->getStart('test-id'));
+ }
+
+ public function test_get_start_returns_null_for_unknown_test(): void
+ {
+ $registry = new TestMemoryRegistry();
+
+ static::assertNull($registry->getStart('unknown'));
+ }
+
+ public function test_set_and_get_start(): void
+ {
+ $registry = new TestMemoryRegistry();
+ $registry->setStart('test-id', 2048);
+
+ static::assertSame(2048, $registry->getStart('test-id'));
+ }
+}