Skip to content

Commit

Permalink
Improve meter test coverage
Browse files Browse the repository at this point in the history
  • Loading branch information
Nevay committed Jul 17, 2022
1 parent c855193 commit 62765bc
Show file tree
Hide file tree
Showing 2 changed files with 262 additions and 3 deletions.
111 changes: 111 additions & 0 deletions tests/Unit/SDK/Metrics/InstrumentTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,12 @@
use OpenTelemetry\SDK\Metrics\Data;
use OpenTelemetry\SDK\Metrics\Data\Temporality;
use OpenTelemetry\SDK\Metrics\Histogram;
use OpenTelemetry\SDK\Metrics\MetricObserver\CallbackDestructor;
use OpenTelemetry\SDK\Metrics\MetricObserver\MultiObserver;
use OpenTelemetry\SDK\Metrics\MetricObserverInterface;
use OpenTelemetry\SDK\Metrics\ObservableCallback;
use OpenTelemetry\SDK\Metrics\ObservableCounter;
use OpenTelemetry\SDK\Metrics\ReferenceCounterInterface;
use OpenTelemetry\SDK\Metrics\StalenessHandler\NoopStalenessHandler;
use OpenTelemetry\SDK\Metrics\Stream\AsynchronousMetricStream;
use OpenTelemetry\SDK\Metrics\Stream\StreamWriter;
Expand Down Expand Up @@ -80,6 +84,33 @@ public function test_asynchronous_counter(): void
), $s->collect($r, 1));
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCounter
*/
public function test_asynchronous_counter_weaken(): void
{
$o = new MultiObserver();
$s = new AsynchronousMetricStream(Attributes::factory(), null, new SumAggregation(true), null, $o, 0);
$c = new ObservableCounter($o, new NoopStalenessHandler());
$r = $s->register(Temporality::CUMULATIVE);

$instance = new class() {
public function __invoke(ObserverInterface $observer)
{
$observer->observe(5);
}
};

$c->observe($instance, true);
$instance = null;

$this->assertEquals(new Data\Sum(
[],
Temporality::CUMULATIVE,
true,
), $s->collect($r, 1));
}

/**
* @covers \OpenTelemetry\SDK\Metrics\UpDownCounter
*/
Expand Down Expand Up @@ -141,4 +172,84 @@ public function test_histogram(): void
Temporality::DELTA,
), $s->collect($r, 1));
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCallback
*/
public function test_observable_callback_releases_on_detach(): void
{
$metricObserver = $this->createMock(MetricObserverInterface::class);
$metricObserver->method('has')->with(1)->willReturnOnConsecutiveCalls(true, false);
$metricObserver->expects($this->once())->method('cancel')->with(1);
$referenceCounter = $this->createMock(ReferenceCounterInterface::class);
$referenceCounter->expects($this->once())->method('release');

$callback = new ObservableCallback($metricObserver, $referenceCounter, 1, null);
$callback->detach();
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCallback
*/
public function test_observable_callback_removes_callback_destructor_token_on_detach(): void
{
$metricObserver = $this->createMock(MetricObserverInterface::class);
$metricObserver->method('has')->with(1)->willReturnOnConsecutiveCalls(true, false);
$referenceCounter = $this->createMock(ReferenceCounterInterface::class);

$callbackDestructor = new CallbackDestructor($metricObserver, $referenceCounter);
$callbackDestructor->tokens[1] = 1;

$callback = new ObservableCallback($metricObserver, $referenceCounter, 1, $callbackDestructor);
$callback->detach();

$this->assertArrayNotHasKey(1, $callbackDestructor->tokens);
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCallback
*/
public function test_observable_callback_does_not_release_on_detach_if_invalid_token(): void
{
$metricObserver = $this->createMock(MetricObserverInterface::class);
$metricObserver->method('has')->with(1)->willReturn(false);
$metricObserver->expects($this->never())->method('cancel')->with(1);
$referenceCounter = $this->createMock(ReferenceCounterInterface::class);
$referenceCounter->expects($this->never())->method('release');

$callback = new ObservableCallback($metricObserver, $referenceCounter, 1, null);
$callback->detach();
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCallback
*/
public function test_observable_callback_acquires_persistent_on_destruct(): void
{
$metricObserver = $this->createMock(MetricObserverInterface::class);
$metricObserver->method('has')->with(1)->willReturn(true);
$referenceCounter = $this->createMock(ReferenceCounterInterface::class);
$referenceCounter->expects($this->once())->method('acquire')->with(true);
$referenceCounter->expects($this->once())->method('release');

/** @noinspection PhpExpressionResultUnusedInspection */
new ObservableCallback($metricObserver, $referenceCounter, 1, null);
}

/**
* @covers \OpenTelemetry\SDK\Metrics\ObservableCallback
*/
public function test_observable_callback_does_not_acquire_persistent_on_destruct_if_callback_destructor_set(): void
{
$metricObserver = $this->createMock(MetricObserverInterface::class);
$metricObserver->method('has')->with(1)->willReturn(true);
$referenceCounter = $this->createMock(ReferenceCounterInterface::class);
$referenceCounter->expects($this->never())->method('acquire')->with(true);

$callbackDestructor = new CallbackDestructor($metricObserver, $referenceCounter);
$callbackDestructor->tokens[1] = 1;

/** @noinspection PhpExpressionResultUnusedInspection */
new ObservableCallback($metricObserver, $referenceCounter, 1, $callbackDestructor);
}
}
154 changes: 151 additions & 3 deletions tests/Unit/SDK/Metrics/MeterTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,26 @@

namespace OpenTelemetry\Tests\Unit\SDK\Metrics;

use function func_get_arg;
use OpenTelemetry\API\Metrics\ObserverInterface;
use OpenTelemetry\SDK\Common\Attribute\Attributes;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScope;
use OpenTelemetry\SDK\Common\Instrumentation\InstrumentationScopeFactory;
use OpenTelemetry\SDK\Common\Time\ClockFactory;
use OpenTelemetry\SDK\Metrics\AggregationInterface;
use OpenTelemetry\SDK\Metrics\DefaultAggregationProviderInterface;
use OpenTelemetry\SDK\Metrics\Instrument;
use OpenTelemetry\SDK\Metrics\InstrumentType;
use OpenTelemetry\SDK\Metrics\MeterProvider;
use OpenTelemetry\SDK\Metrics\MetricFactoryInterface;
use OpenTelemetry\SDK\Metrics\MetricObserverInterface;
use OpenTelemetry\SDK\Metrics\MetricReaderInterface;
use OpenTelemetry\SDK\Metrics\MetricSourceRegistryInterface;
use OpenTelemetry\SDK\Metrics\MetricWriterInterface;
use OpenTelemetry\SDK\Metrics\StalenessHandler\ImmediateStalenessHandlerFactory;
use OpenTelemetry\SDK\Metrics\View\CriteriaViewRegistry;
use OpenTelemetry\SDK\Metrics\ViewProjection;
use OpenTelemetry\SDK\Metrics\ViewRegistryInterface;
use OpenTelemetry\SDK\Resource\ResourceInfoFactory;
use PHPUnit\Framework\TestCase;
use Prophecy\Argument;
Expand Down Expand Up @@ -152,6 +162,57 @@ public function test_create_observable_up_down_counter(): void
$meter->createObservableUpDownCounter('name', 'unit', 'description');
}

public function test_create_observable_counter_register_permanent_callback(): void
{
$callable = fn (ObserverInterface $observer) => $observer->observe(0);
$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->method('createAsynchronousObserver')
->willReturnCallback(function () use ($callable) {
$observer = $this->createMock(MetricObserverInterface::class);
$observer->expects($this->once())->method('observe')->with($callable);

return $observer;
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory);
$meter = $meterProvider->getMeter('test');
$meter->createObservableCounter('name', 'unit', 'description', $callable);
}

public function test_create_observable_gauge_register_permanent_callback(): void
{
$callable = fn (ObserverInterface $observer) => $observer->observe(0);
$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->method('createAsynchronousObserver')
->willReturnCallback(function () use ($callable) {
$observer = $this->createMock(MetricObserverInterface::class);
$observer->expects($this->once())->method('observe')->with($callable);

return $observer;
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory);
$meter = $meterProvider->getMeter('test');
$meter->createObservableGauge('name', 'unit', 'description', $callable);
}

public function test_create_observable_up_down_counter_register_permanent_callback(): void
{
$callable = fn (ObserverInterface $observer) => $observer->observe(0);
$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->method('createAsynchronousObserver')
->willReturnCallback(function () use ($callable) {
$observer = $this->createMock(MetricObserverInterface::class);
$observer->expects($this->once())->method('observe')->with($callable);

return $observer;
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory);
$meter = $meterProvider->getMeter('test');
$meter->createObservableUpDownCounter('name', 'unit', 'description', $callable);
}

/** @noinspection PhpUnusedLocalVariableInspection */
public function test_reuses_writer_when_not_stale(): void
{
Expand Down Expand Up @@ -242,19 +303,106 @@ public function test_releases_observer_on_stale(): void
$meter->createObservableCounter('name', 'unit', 'description');
}

private function createMeterProviderForMetricFactory(MetricFactoryInterface $metricFactory): MeterProvider
public function test_uses_view_registry_to_create_views(): void
{
$aggregation = $this->createMock(AggregationInterface::class);

$metricReader = $this->createMock(MeterMetricReaderInterface::class);

$viewRegistry = $this->createMock(ViewRegistryInterface::class);
$viewRegistry->method('find')->willReturn([
new ViewProjection('view-1', null, null, null, $aggregation),
new ViewProjection('view-2', null, null, null, $aggregation),
]);

$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->expects($this->exactly(1))->method('createSynchronousWriter')
->willReturnCallback(function () use ($aggregation): MetricWriterInterface {
[[$v1], [$v2]] = [...func_get_arg(4)];

$this->assertEquals(new ViewProjection('view-1', null, null, null, $aggregation), $v1);
$this->assertEquals(new ViewProjection('view-2', null, null, null, $aggregation), $v2);

return $this->createMock(MetricWriterInterface::class);
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory, $viewRegistry, [$metricReader]);
$meter = $meterProvider->getMeter('test');
$meter->createCounter('name');
}

public function test_uses_default_aggregation_if_view_aggregation_null(): void
{
$aggregation = $this->createMock(AggregationInterface::class);

$metricReader = $this->createMock(MeterMetricReaderInterface::class);
$metricReader->method('defaultAggregation')->willReturn($aggregation);

$viewRegistry = $this->createMock(ViewRegistryInterface::class);
$viewRegistry->method('find')->willReturn([
new ViewProjection('view-1', null, null, null, null),
]);

$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->expects($this->exactly(1))->method('createSynchronousWriter')
->willReturnCallback(function () use ($aggregation): MetricWriterInterface {
[[$v1]] = [...func_get_arg(4)];

$this->assertEquals(new ViewProjection('view-1', null, null, null, $aggregation), $v1);

return $this->createMock(MetricWriterInterface::class);
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory, $viewRegistry, [$metricReader]);
$meter = $meterProvider->getMeter('test');
$meter->createCounter('name');
}

public function test_uses_default_view_if_null_views_returned(): void
{
$aggregation = $this->createMock(AggregationInterface::class);

$metricReader = $this->createMock(MeterMetricReaderInterface::class);
$metricReader->method('defaultAggregation')->willReturn($aggregation);

$viewRegistry = $this->createMock(ViewRegistryInterface::class);
$viewRegistry->method('find')->willReturn(null);

$metricFactory = $this->createMock(MetricFactoryInterface::class);
$metricFactory->expects($this->exactly(1))->method('createSynchronousWriter')
->willReturnCallback(function () use ($aggregation): MetricWriterInterface {
[[$v1]] = [...func_get_arg(4)];

$this->assertEquals(new ViewProjection('name', null, null, null, $aggregation), $v1);

return $this->createMock(MetricWriterInterface::class);
});

$meterProvider = $this->createMeterProviderForMetricFactory($metricFactory, $viewRegistry, [$metricReader]);
$meter = $meterProvider->getMeter('test');
$meter->createCounter('name');
}

/**
* @param iterable<MetricReaderInterface&MetricSourceRegistryInterface&DefaultAggregationProviderInterface> $metricReaders
*/
private function createMeterProviderForMetricFactory(MetricFactoryInterface $metricFactory, ViewRegistryInterface $viewRegistry = null, iterable $metricReaders = []): MeterProvider
{
return new MeterProvider(
null,
ResourceInfoFactory::emptyResource(),
ClockFactory::getDefault(),
Attributes::factory(),
new InstrumentationScopeFactory(Attributes::factory()),
[],
new CriteriaViewRegistry(),
$metricReaders,
$viewRegistry ?? new CriteriaViewRegistry(),
null,
new ImmediateStalenessHandlerFactory(),
$metricFactory,
);
}
}

interface MeterMetricReaderInterface extends MetricReaderInterface, MetricSourceRegistryInterface, DefaultAggregationProviderInterface
{
}

0 comments on commit 62765bc

Please sign in to comment.