@@ -785,6 +785,149 @@ TEST(RetryCountTest, ThreeAttempts) {
785785 clone->OnDone (otel_context, {clock->Now (), Status{StatusCode::kOk , " ok" }});
786786}
787787
788+ TEST (FirstResponseLatency, Success) {
789+ auto mock_histogram = std::make_unique<MockHistogram<double >>();
790+ EXPECT_CALL (
791+ *mock_histogram,
792+ Record (A<double >(), A<opentelemetry::common::KeyValueIterable const &>(),
793+ A<opentelemetry::context::Context const &>()))
794+ .WillOnce ([](double value,
795+ opentelemetry::common::KeyValueIterable const & attributes,
796+ opentelemetry::context::Context const &) {
797+ EXPECT_THAT (value, Eq (2.0 ));
798+ EXPECT_THAT (
799+ MakeAttributesMap (attributes),
800+ UnorderedElementsAre (
801+ Pair (" project_id" , " my-project-id" ),
802+ Pair (" instance" , " my-instance" ), Pair (" cluster" , " my-cluster" ),
803+ Pair (" table" , " my-table" ), Pair (" zone" , " my-zone" ),
804+ Pair (" method" , " my-method" ), Pair (" status" , " OK" ),
805+ Pair (" client_name" , " my-client-name" ),
806+ Pair (" client_uid" , " my-client-uid" ),
807+ Pair (" app_profile" , " my-app-profile" )));
808+ });
809+
810+ opentelemetry::nostd::shared_ptr<MockMeter> mock_meter =
811+ std::make_shared<MockMeter>();
812+ EXPECT_CALL (*mock_meter, CreateDoubleHistogram)
813+ .WillOnce ([mock = std::move (mock_histogram)](
814+ opentelemetry::nostd::string_view name,
815+ opentelemetry::nostd::string_view,
816+ opentelemetry::nostd::string_view) mutable {
817+ EXPECT_THAT (name, Eq (" first_response_latencies" ));
818+ return std::move (mock);
819+ });
820+
821+ opentelemetry::nostd::shared_ptr<MockMeterProvider> mock_provider =
822+ std::make_shared<MockMeterProvider>();
823+ EXPECT_CALL (*mock_provider, GetMeter)
824+ #if OPENTELEMETRY_ABI_VERSION_NO >= 2
825+ .WillOnce ([&](opentelemetry::nostd::string_view scope,
826+ opentelemetry::nostd::string_view scope_version,
827+ opentelemetry::nostd::string_view,
828+ opentelemetry::common::KeyValueIterable const *) mutable {
829+ #else
830+ .WillOnce ([&](opentelemetry::nostd::string_view scope,
831+ opentelemetry::nostd::string_view scope_version,
832+ opentelemetry::nostd::string_view) mutable {
833+ #endif
834+ EXPECT_THAT (scope, Eq (" my-instrument-scope" ));
835+ EXPECT_THAT (scope_version, Eq (" v1" ));
836+ return mock_meter;
837+ });
838+
839+ FirstResponseLatency first_response_latency (" my-instrument-scope" ,
840+ mock_provider);
841+ ResourceLabels resource_labels{" my-project-id" , " my-instance" , " my-table" , " " ,
842+ " " };
843+ DataLabels data_labels{" my-method" , " my-streaming" , " my-client-name" ,
844+ " my-client-uid" , " my-app-profile" , " " };
845+ auto clone = first_response_latency.clone (resource_labels, data_labels);
846+
847+ grpc::ClientContext client_context;
848+ SetClusterZone (client_context, " my-cluster" , " my-zone" );
849+
850+ auto otel_context = opentelemetry::context::RuntimeContext::GetCurrent ();
851+ auto clock = std::make_shared<FakeSteadyClock>();
852+
853+ // Verify that only the first response is recorded.
854+ clock->SetTime (std::chrono::steady_clock::now ());
855+ clone->PreCall (otel_context, {clock->Now (), true });
856+ clock->AdvanceTime (std::chrono::milliseconds (2 ));
857+ clone->ElementDelivery (otel_context, {clock->Now (), true });
858+ clock->AdvanceTime (std::chrono::milliseconds (5 ));
859+ clone->ElementDelivery (otel_context, {clock->Now (), false });
860+ clock->AdvanceTime (std::chrono::milliseconds (5 ));
861+ clone->PostCall (otel_context, client_context,
862+ {clock->Now (), Status{StatusCode::kOk , " ok" }});
863+ clock->AdvanceTime (std::chrono::milliseconds (5 ));
864+ clone->OnDone (otel_context, {clock->Now (), Status{StatusCode::kOk , " ok" }});
865+ }
866+
867+ TEST (FirstResponseLatency, NoDataReceived) {
868+ auto mock_histogram = std::make_unique<MockHistogram<double >>();
869+ EXPECT_CALL (
870+ *mock_histogram,
871+ Record (A<double >(), A<opentelemetry::common::KeyValueIterable const &>(),
872+ A<opentelemetry::context::Context const &>()))
873+ .Times (0 );
874+
875+ opentelemetry::nostd::shared_ptr<MockMeter> mock_meter =
876+ std::make_shared<MockMeter>();
877+ EXPECT_CALL (*mock_meter, CreateDoubleHistogram)
878+ .WillOnce ([mock = std::move (mock_histogram)](
879+ opentelemetry::nostd::string_view name,
880+ opentelemetry::nostd::string_view,
881+ opentelemetry::nostd::string_view) mutable {
882+ EXPECT_THAT (name, Eq (" first_response_latencies" ));
883+ return std::move (mock);
884+ });
885+
886+ opentelemetry::nostd::shared_ptr<MockMeterProvider> mock_provider =
887+ std::make_shared<MockMeterProvider>();
888+ EXPECT_CALL (*mock_provider, GetMeter)
889+ #if OPENTELEMETRY_ABI_VERSION_NO >= 2
890+ .WillOnce ([&](opentelemetry::nostd::string_view scope,
891+ opentelemetry::nostd::string_view scope_version,
892+ opentelemetry::nostd::string_view,
893+ opentelemetry::common::KeyValueIterable const *) mutable {
894+ #else
895+ .WillOnce ([&](opentelemetry::nostd::string_view scope,
896+ opentelemetry::nostd::string_view scope_version,
897+ opentelemetry::nostd::string_view) mutable {
898+ #endif
899+ EXPECT_THAT (scope, Eq (" my-instrument-scope" ));
900+ EXPECT_THAT (scope_version, Eq (" v1" ));
901+ return mock_meter;
902+ });
903+
904+ FirstResponseLatency first_response_latency (" my-instrument-scope" ,
905+ mock_provider);
906+ ResourceLabels resource_labels{" my-project-id" , " my-instance" , " my-table" , " " ,
907+ " " };
908+ DataLabels data_labels{" my-method" , " my-streaming" , " my-client-name" ,
909+ " my-client-uid" , " my-app-profile" , " " };
910+ auto clone = first_response_latency.clone (resource_labels, data_labels);
911+
912+ grpc::ClientContext client_context;
913+ SetClusterZone (client_context, " my-cluster" , " my-zone" );
914+
915+ auto otel_context = opentelemetry::context::RuntimeContext::GetCurrent ();
916+ auto clock = std::make_shared<FakeSteadyClock>();
917+
918+ // ElementDelivery is not called.
919+ clock->SetTime (std::chrono::steady_clock::now ());
920+ clone->PreCall (otel_context, {clock->Now (), true });
921+ clock->AdvanceTime (std::chrono::milliseconds (5 ));
922+ clone->PostCall (
923+ otel_context, client_context,
924+ {clock->Now (), Status{StatusCode::kDeadlineExceeded , " timeout" }});
925+ clock->AdvanceTime (std::chrono::milliseconds (5 ));
926+ clone->OnDone (
927+ otel_context,
928+ {clock->Now (), Status{StatusCode::kDeadlineExceeded , " timeout" }});
929+ }
930+
788931} // namespace
789932GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_END
790933} // namespace bigtable_internal
0 commit comments