Skip to content

Commit

Permalink
Finish tests for OtlpHttpExporter
Browse files Browse the repository at this point in the history
Signed-off-by: owent <[email protected]>
  • Loading branch information
owent committed Jun 5, 2021
1 parent 4ca193a commit 69e4f24
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@ struct OtlpHttpExporterOptions
// By default, post json data
HttpRequestContentType content_type = HttpRequestContentType::kJson;

// TODO: By default when false, set CURLOPT_SSL_VERIFYPEER to false
// If convert bytes into hex. By default, we will convert all bytes but id into base64
// This option is ignored if content_type is not kJson
JsonBytesMappingKind json_bytes_mapping = JsonBytesMappingKind::kHexId;
Expand All @@ -64,8 +63,8 @@ struct OtlpHttpExporterOptions
// Whether to print the status of the exporter in the console
bool console_debug = false;

// Maximum time to wait for response after sending http request(milliseconds)
int response_timeout = 30000;
// TODO: Enable/disable to verify SSL certificate
// TODO: Reuqest timeout
};

/**
Expand Down
157 changes: 93 additions & 64 deletions exporters/otlp/test/otlp_http_exporter_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# include "opentelemetry/exporters/otlp/protobuf_include_suffix.h"

# include "opentelemetry/ext/http/server/http_server.h"
# include "opentelemetry/sdk/trace/simple_processor.h"
# include "opentelemetry/sdk/trace/batch_span_processor.h"
# include "opentelemetry/sdk/trace/tracer_provider.h"
# include "opentelemetry/trace/provider.h"

Expand All @@ -22,14 +22,18 @@

using namespace testing;

namespace http_client = opentelemetry::ext::http::client;

OPENTELEMETRY_BEGIN_NAMESPACE
namespace exporter
{
namespace otlp
{

template <class T, size_t N>
static nostd::span<T, N> MakeSpan(T (&array)[N])
{
return nostd::span<T, N>(array);
}

class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::HttpRequestCallback
{
protected:
Expand Down Expand Up @@ -74,14 +78,24 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::
virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request,
HTTP_SERVER_NS::HttpResponse &response) override
{
const std::string *request_content_type = nullptr;
{
auto it = request.headers.find("Content-Type");
if (it != request.headers.end())
{
request_content_type = &it->second;
}
}

if (request.uri == kDefaultTracePath)
{
response.headers["Content-Type"] = "application/json";
std::unique_lock<std::mutex> lk(mtx_requests);
if (request.headers["Content-Type"] == kHttpBinaryContentType)
if (nullptr != request_content_type && *request_content_type == kHttpBinaryContentType)
{
opentelemetry::proto::collector::trace::v1::ExportTraceServiceRequest request_body;
if (request_body.ParseFromArray(&request.content[0], request.content.size()))
if (request_body.ParseFromArray(&request.content[0],
static_cast<int>(request.content.size())))
{
received_requests_binary_.push_back(request_body);
response.body = "{\"code\": 0, \"message\": \"success\"}";
Expand All @@ -92,7 +106,7 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::
return 400;
}
}
else if (request.headers["Content-Type"] == kHttpJsonContentType)
else if (nullptr != request_content_type && *request_content_type == kHttpJsonContentType)
{
auto json = nlohmann::json::parse(request.content, nullptr, false);
response.headers["Content-Type"] = "application/json";
Expand Down Expand Up @@ -124,25 +138,29 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::
}
}

bool waitForRequests(unsigned timeOutSec, unsigned expected_count = 1)
bool waitForRequests(unsigned timeOutSec, size_t expected_count = 1)
{
std::unique_lock<std::mutex> lk(mtx_requests);
if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), [&] {
return received_requests_json_.size() + received_requests_binary_.size() >=
expected_count;
}))
if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec),
[&] { return getCurrentRequestCount() >= expected_count; }))
{
return true;
}
return false;
}

size_t getCurrentRequestCount() const
{
return received_requests_json_.size() + received_requests_binary_.size();
}

public:
std::unique_ptr<sdk::trace::SpanExporter> GetExporter(HttpRequestContentType content_type)
{
OtlpHttpExporterOptions opts;
opts.url = server_address_;
opts.content_type = content_type;
opts.url = server_address_;
opts.content_type = content_type;
opts.console_debug = true;
return std::unique_ptr<sdk::trace::SpanExporter>(new OtlpHttpExporter(opts));
}

Expand All @@ -153,38 +171,14 @@ class OtlpHttpExporterTestPeer : public ::testing::Test, public HTTP_SERVER_NS::
}
};

// Call Export() directly
TEST_F(OtlpHttpExporterTestPeer, ExportUnitTest)
{
auto exporter = GetExporter(HttpRequestContentType::kJson);

auto recordable_1 = exporter->MakeRecordable();
recordable_1->SetName("Test span 1");
auto recordable_2 = exporter->MakeRecordable();
recordable_2->SetName("Test span 2");

// Test successful RPC
nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_1(&recordable_1, 1);
EXPECT_CALL(*mock_stub, Export(_, _, _)).Times(Exactly(1)).WillOnce(Return(grpc::Status::OK));
auto result = exporter->Export(batch_1);
EXPECT_EQ(sdk::common::ExportResult::kSuccess, result);

// Test failed RPC
nostd::span<std::unique_ptr<sdk::trace::Recordable>> batch_2(&recordable_2, 1);
EXPECT_CALL(*mock_stub, Export(_, _, _))
.Times(Exactly(1))
.WillOnce(Return(grpc::Status::CANCELLED));
result = exporter->Export(batch_2);
EXPECT_EQ(sdk::common::ExportResult::kFailure, result);
}

// Create spans, let processor call Export()
TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest)
{
auto exporter = GetExporter(HttpRequestContentType::kJson);
size_t old_count = getCurrentRequestCount();
auto exporter = GetExporter(HttpRequestContentType::kJson);

opentelemetry::sdk::resource::ResourceAttributes resource_attributes = {
{"service.name", 'unit_test_service'}, {"tenant.id", 'test_user'}};
{"service.name", "unit_test_service"}, {"tenant.id", "test_user"}};
resource_attributes["bool_value"] = true;
resource_attributes["int32_value"] = static_cast<int32_t>(1);
resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
Expand All @@ -200,29 +194,47 @@ TEST_F(OtlpHttpExporterTestPeer, ExportJsonIntegrationTest)
resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);

auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
new sdk::trace::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<trace::TracerProvider>(
auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(new sdk::trace::BatchSpanProcessor(
std::move(exporter),
sdk::trace::BatchSpanProcessorOptions{5, std::chrono::milliseconds(256), 5}));
auto provider = nostd::shared_ptr<trace::TracerProvider>(
new sdk::trace::TracerProvider(std::move(processor), resource));
auto tracer = provider->GetTracer("test");

EXPECT_CALL(*mock_stub, Export(_, _, _))
.Times(AtLeast(1))
.WillRepeatedly(Return(grpc::Status::OK));
std::string report_trace_id;
{
char trace_id_hex[2 * opentelemetry::trace::TraceId::kSize] = {0};
auto tracer = provider->GetTracer("test");
auto parent_span = tracer->StartSpan("Test parent span");

opentelemetry::trace::StartSpanOptions child_span_opts = {};
child_span_opts.parent = parent_span->GetContext();

auto child_span = tracer->StartSpan("Test child span", child_span_opts);
child_span->End();
parent_span->End();

auto parent_span = tracer->StartSpan("Test parent span");
auto child_span = tracer->StartSpan("Test child span");
child_span->End();
parent_span->End();
child_span_opts.parent.trace_id().ToLowerBase16(MakeSpan(trace_id_hex));
report_trace_id.assign(trace_id_hex, sizeof(trace_id_hex));
}

ASSERT_TRUE(waitForRequests(2, old_count + 1));
auto check_json = received_requests_json_.back();
auto resource_span = *check_json["resource_spans"].begin();
auto instrumentation_library_span = *resource_span["instrumentation_library_spans"].begin();
auto span = *instrumentation_library_span["spans"].begin();
auto received_trace_id = span["trace_id"].get<std::string>();
EXPECT_EQ(received_trace_id, report_trace_id);
}

// Create spans, let processor call Export()
TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest)
{
size_t old_count = getCurrentRequestCount();

auto exporter = GetExporter(HttpRequestContentType::kBinary);

opentelemetry::sdk::resource::ResourceAttributes resource_attributes = {
{"service.name", 'unit_test_service'}, {"tenant.id", 'test_user'}};
{"service.name", "unit_test_service"}, {"tenant.id", "test_user"}};
resource_attributes["bool_value"] = true;
resource_attributes["int32_value"] = static_cast<int32_t>(1);
resource_attributes["uint32_value"] = static_cast<uint32_t>(2);
Expand All @@ -238,20 +250,37 @@ TEST_F(OtlpHttpExporterTestPeer, ExportBinaryIntegrationTest)
resource_attributes["vec_string_value"] = std::vector<std::string>{"vector", "string"};
auto resource = opentelemetry::sdk::resource::Resource::Create(resource_attributes);

auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(
new sdk::trace::SimpleSpanProcessor(std::move(exporter)));
auto provider = nostd::shared_ptr<trace::TracerProvider>(
auto processor = std::unique_ptr<sdk::trace::SpanProcessor>(new sdk::trace::BatchSpanProcessor(
std::move(exporter),
sdk::trace::BatchSpanProcessorOptions{5, std::chrono::milliseconds(256), 5}));
auto provider = nostd::shared_ptr<trace::TracerProvider>(
new sdk::trace::TracerProvider(std::move(processor), resource));
auto tracer = provider->GetTracer("test");

EXPECT_CALL(*mock_stub, Export(_, _, _))
.Times(AtLeast(1))
.WillRepeatedly(Return(grpc::Status::OK));
std::string report_trace_id;
{
uint8_t trace_id_binary[opentelemetry::trace::TraceId::kSize] = {0};
auto tracer = provider->GetTracer("test");
auto parent_span = tracer->StartSpan("Test parent span");

opentelemetry::trace::StartSpanOptions child_span_opts = {};
child_span_opts.parent = parent_span->GetContext();

auto parent_span = tracer->StartSpan("Test parent span");
auto child_span = tracer->StartSpan("Test child span");
child_span->End();
parent_span->End();
auto child_span = tracer->StartSpan("Test child span", child_span_opts);
child_span->End();
parent_span->End();

child_span_opts.parent.trace_id().CopyBytesTo(MakeSpan(trace_id_binary));
report_trace_id.assign(reinterpret_cast<char *>(trace_id_binary), sizeof(trace_id_binary));
}

ASSERT_TRUE(waitForRequests(2, old_count + 1));

auto received_trace_id = received_requests_binary_.back()
.resource_spans(0)
.instrumentation_library_spans(0)
.spans(0)
.trace_id();
EXPECT_EQ(received_trace_id, report_trace_id);
}

// Test exporter configuration options
Expand All @@ -273,7 +302,7 @@ TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest)
}

// Test exporter configuration options with json_bytes_mapping=JsonBytesMappingKind::kHex
TEST_F(OtlpHttpExporterTestPeer, ConfigUseJsonNameTest)
TEST_F(OtlpHttpExporterTestPeer, ConfigJsonBytesMappingTest)
{
OtlpHttpExporterOptions opts;
opts.json_bytes_mapping = JsonBytesMappingKind::kHex;
Expand Down

0 comments on commit 69e4f24

Please sign in to comment.