1616#include " google/cloud/storage/async/object_descriptor_connection.h"
1717#include " google/cloud/storage/mocks/mock_async_object_descriptor_connection.h"
1818#include " google/cloud/storage/mocks/mock_async_reader_connection.h"
19+ #include " google/cloud/internal/opentelemetry.h"
1920#include " google/cloud/opentelemetry_options.h"
2021#include " google/cloud/options.h"
2122#include " google/cloud/testing_util/opentelemetry_matchers.h"
2223#include < gmock/gmock-matchers.h>
2324#include < gmock/gmock.h>
2425#include < opentelemetry/semconv/incubating/thread_attributes.h>
26+ #include < memory>
27+ #include < utility>
2528
2629namespace google {
2730namespace cloud {
2831namespace storage_internal {
2932GOOGLE_CLOUD_CPP_INLINE_NAMESPACE_BEGIN
3033namespace {
3134
32- using ReadResponse =
33- ::google::cloud::storage::AsyncReaderConnection::ReadResponse;
3435using ::google::cloud::storage::ObjectDescriptorConnection;
35- using ::google::cloud::storage::ReadPayload;
3636using ::google::cloud::storage_mocks::MockAsyncObjectDescriptorConnection;
3737using ::google::cloud::storage_mocks::MockAsyncReaderConnection;
3838using ::google::cloud::testing_util::EventNamed;
3939using ::google::cloud::testing_util::InstallSpanCatcher;
4040using ::google::cloud::testing_util::OTelAttribute;
41- using ::google::cloud::testing_util::OTelContextCaptured;
4241using ::google::cloud::testing_util::PromiseWithOTelContext;
4342using ::google::cloud::testing_util::SpanEventAttributesAre;
43+ using ::google::cloud::testing_util::SpanEventsAre;
44+ using ::google::cloud::testing_util::SpanHasEvents;
4445using ::google::cloud::testing_util::SpanHasInstrumentationScope;
4546using ::google::cloud::testing_util::SpanKindIsClient;
4647using ::google::cloud::testing_util::SpanNamed;
4748using ::google::cloud::testing_util::SpanWithStatus;
48- using ::google::cloud::testing_util::ThereIsAnActiveSpan;
4949using ::testing::_;
50-
51- // A helper to set expectations on a mock async reader. It captures the OTel
52- // context and returns a future that can be controlled by the test.
53- auto expect_context = [](auto & p) {
54- return [&p] {
55- EXPECT_TRUE (ThereIsAnActiveSpan ());
56- EXPECT_TRUE (OTelContextCaptured ());
57- return p.get_future ();
58- };
59- };
60-
61- // A helper to be used in a `.then()` clause. It verifies the OTel context
62- // has been detached before the user receives the result.
63- auto expect_no_context = [](auto f) {
64- auto t = f.get ();
65- EXPECT_FALSE (ThereIsAnActiveSpan ());
66- EXPECT_FALSE (OTelContextCaptured ());
67- return t;
68- };
50+ using ::testing::AllOf;
51+ using ::testing::ElementsAre;
6952
7053TEST (ObjectDescriptorConnectionTracing, Read) {
7154 namespace sc = ::opentelemetry::semconv;
@@ -79,7 +62,7 @@ TEST(ObjectDescriptorConnectionTracing, Read) {
7962 return std::make_unique<MockAsyncReaderConnection>();
8063 });
8164 auto actual = MakeTracingObjectDescriptorConnection (
82- internal::MakeSpan (" test-span-name" ), std::move (mock));
65+ internal::MakeSpan (" test-span-name" ), std::move (mock), " test-bucket " );
8366 auto f1 = actual->Read (ObjectDescriptorConnection::ReadParams{100 , 200 });
8467
8568 actual.reset ();
@@ -98,30 +81,39 @@ TEST(ObjectDescriptorConnectionTracing, Read) {
9881 OTelAttribute<std::string>(sc::thread::kThreadId , _)))))));
9982}
10083
101- TEST (ObjectDescriptorConnectionTracing, ReadThenRead) {
84+ TEST (ObjectDescriptorConnectionTracing,
85+ SingleReadRangeCompletedDoesNotEndOpenSpan) {
10286 namespace sc = ::opentelemetry::semconv;
10387 auto span_catcher = InstallSpanCatcher ();
10488
10589 auto mock_connection =
10690 std::make_shared<MockAsyncObjectDescriptorConnection>();
107- auto * mock_reader_ptr = new MockAsyncReaderConnection;
108- PromiseWithOTelContext<ReadResponse> p;
109- EXPECT_CALL (*mock_reader_ptr , Read).WillOnce (expect_context (p) );
91+ auto mock_reader = std::make_unique< MockAsyncReaderConnection>() ;
92+ PromiseWithOTelContext<storage::AsyncReaderConnection:: ReadResponse> p;
93+ EXPECT_CALL (*mock_reader , Read).WillOnce ([&p] { return p. get_future (); } );
11094
11195 EXPECT_CALL (*mock_connection, Read)
112- .WillOnce ([&](ObjectDescriptorConnection::ReadParams) {
113- return std::unique_ptr<storage::AsyncReaderConnection>(mock_reader_ptr);
96+ .WillOnce ([&, r = std::move (mock_reader)](
97+ ObjectDescriptorConnection::ReadParams p) mutable {
98+ EXPECT_EQ (p.start , 100 );
99+ EXPECT_EQ (p.length , 200 );
100+ return std::move (r);
114101 });
115102
116103 auto connection = MakeTracingObjectDescriptorConnection (
117- internal::MakeSpan (" test-span" ), std::move (mock_connection));
104+ internal::MakeSpan (" test-span" ), std::move (mock_connection),
105+ " test-bucket" );
118106
119- auto reader = connection->Read ({});
120- auto f = reader->Read ().then (expect_no_context);
121- p.set_value (ReadPayload (" test-payload" ).set_offset (123 ));
107+ auto reader = connection->Read ({100 , 200 });
108+ auto f = reader->Read ();
109+ // Simulate stream completion (EOF)
110+ p.set_value (Status{});
122111 (void )f.get ();
123112
124- connection.reset (); // End the span
113+ // Before resetting the connection, the Open span must NOT be ended yet.
114+ EXPECT_THAT (span_catcher->GetSpans (), ::testing::IsEmpty ());
115+
116+ connection.reset (); // End the span now
125117
126118 auto spans = span_catcher->GetSpans ();
127119 EXPECT_THAT (
@@ -130,21 +122,12 @@ TEST(ObjectDescriptorConnectionTracing, ReadThenRead) {
130122 SpanNamed (" test-span" ),
131123 SpanWithStatus (opentelemetry::trace::StatusCode::kOk ),
132124 SpanHasInstrumentationScope (), SpanKindIsClient (),
133- SpanEventsAre (
134- AllOf (EventNamed (" gl-cpp.open.read" ),
135- SpanEventAttributesAre (
136- OTelAttribute<std::int64_t >(" read-length" , 0 ),
137- OTelAttribute<std::int64_t >(" read-start" , 0 ),
138- OTelAttribute<std::string>(sc::thread::kThreadId , _))),
139- AllOf (EventNamed (" gl-cpp.read" ),
140- SpanEventAttributesAre (
141- OTelAttribute<std::int64_t >(" message.starting_offset" ,
142- 123 ),
143- OTelAttribute<std::string>(sc::thread::kThreadId , _),
144- OTelAttribute<std::int64_t >(" rpc.message.id" , 1 ),
145- // THIS WAS THE MISSING ATTRIBUTE:
146- OTelAttribute<std::string>(" rpc.message.type" ,
147- " RECEIVED" )))))));
125+ SpanEventsAre (AllOf (
126+ EventNamed (" gl-cpp.open.read" ),
127+ SpanEventAttributesAre (
128+ OTelAttribute<std::int64_t >(" read-length" , 200 ),
129+ OTelAttribute<std::int64_t >(" read-start" , 100 ),
130+ OTelAttribute<std::string>(sc::thread::kThreadId , _)))))));
148131}
149132
150133} // namespace
0 commit comments