blob: d2075172e6e01e5318c97fdd4a91b9aa1633e271 [file] [log] [blame]
// Copyright 2021 The Verible Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "common/lsp/message-stream-splitter.h"
#include <algorithm>
#include "absl/status/status.h"
#include "absl/strings/string_view.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::HasSubstr;
namespace verible {
namespace lsp {
TEST(MessageStreamSplitterTest, NotRegisteredMessageProcessor) {
MessageStreamSplitter s(4096);
// We need to have had a message processor registered before, otherwise
// the read would not know where to send results.
auto status = s.PullFrom([](char *, int) { return 0; });
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
}
// A stream simulator that is pre-filled with data and allows to
// simulate partial reads.
class DataStreamSimulator {
public:
DataStreamSimulator(absl::string_view content, int max_chunk = -1)
: content_(content), max_chunk_(max_chunk) {}
int read(char *buf, int size) {
if (max_chunk_ > 0 && size > max_chunk_) size = max_chunk_;
size = std::min(size, (int)content_.length() - read_pos_);
memcpy(buf, content_.data() + read_pos_, size);
read_pos_ += size;
return size;
}
private:
const std::string content_;
const int max_chunk_;
int read_pos_ = 0;
};
TEST(MessageStreamSplitterTest, LenientHeaderNewline) {
for (const bool strict_header_crlf : {false, true}) {
MessageStreamSplitter header_test(256, strict_header_crlf);
int count_call = 0;
header_test.SetMessageProcessor(
[&count_call](absl::string_view, absl::string_view body) {
EXPECT_EQ(std::string(body), "x");
++count_call;
});
DataStreamSimulator two_messages(
"Content-Length: 1\r\n\r\nx" // this first one will always parse
"Content-Length: 1\n\nx"); // this will only parse if !strict
absl::Status status = absl::OkStatus();
while (status.ok()) {
status = header_test.PullFrom(
[&](char *buf, int size) { return two_messages.read(buf, size); });
}
if (strict_header_crlf) {
// The second message never contained \r\n\r\n, still pending at end.
EXPECT_EQ(status.code(), absl::StatusCode::kDataLoss) << status;
} else {
// An expected 'read everything reached EOF' status.
EXPECT_EQ(status.code(), absl::StatusCode::kUnavailable) << status;
}
EXPECT_EQ(count_call, strict_header_crlf ? 1 : 2) << status;
}
}
TEST(MessageStreamSplitterTest, CompleteReadValidMessage) {
static constexpr absl::string_view kHeader = "Content-Length: 3\r\n\r\n";
static constexpr absl::string_view kBody = "foo";
DataStreamSimulator stream(absl::StrCat(kHeader, kBody));
MessageStreamSplitter s(4096);
int processor_call_count = 0;
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
++processor_call_count;
EXPECT_EQ(std::string(header), kHeader);
EXPECT_EQ(std::string(body), kBody);
});
auto status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
EXPECT_TRUE(status.ok());
EXPECT_EQ(processor_call_count, 1);
// Calling more read will report EOF as we have finished our data.
// This is reported as kUnavailable, the expected status code in this case.
status = s.PullFrom([&](char *buf, int size) { //
return stream.read(buf, size);
});
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kUnavailable);
EXPECT_EQ(processor_call_count, 1); // No additional calls recorded here.
}
TEST(MessageStreamSplitterTest, BufferSizeReallocated) {
static constexpr absl::string_view kHeader = "Content-Length: 3\r\n\r\n";
static constexpr absl::string_view kBody = "foo";
DataStreamSimulator stream(absl::StrCat(kHeader, kBody));
MessageStreamSplitter s(2); // Start with way too small buffer
int processor_call_count = 0;
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
EXPECT_EQ(std::string(body), "foo");
++processor_call_count;
});
absl::Status status = absl::OkStatus();
while (status.ok()) {
status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
}
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kUnavailable); // regular EOF
EXPECT_EQ(processor_call_count, 1);
}
TEST(MessageStreamSplitterTest, StreamDoesNotContainCompleteData) {
static constexpr absl::string_view kHeader = "Content-Length: 3\r\n\r\n";
static constexpr absl::string_view kBody = "fo"; // <- too short
DataStreamSimulator stream(absl::StrCat(kHeader, kBody));
MessageStreamSplitter s(4096);
int processor_call_count = 0;
s.SetMessageProcessor(
[&](absl::string_view, absl::string_view) { ++processor_call_count; });
absl::Status status = absl::OkStatus();
while (status.ok()) {
status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
}
// We reached EOF, but we still have data pending. Reported as data loss.
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kDataLoss);
EXPECT_EQ(processor_call_count, 0);
}
TEST(MessageStreamSplitterTest, CompleteReadMultipleMessages) {
static constexpr absl::string_view kHeader = "Content-Length: 3\r\n\r\n";
static constexpr absl::string_view kBody[2] = {"foo", "bar"};
DataStreamSimulator stream(
absl::StrCat(kHeader, kBody[0], kHeader, kBody[1]));
MessageStreamSplitter s(4096);
int processor_call_count = 0;
// We expect one call per complete header/body pair.
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
EXPECT_EQ(std::string(header), kHeader);
EXPECT_EQ(std::string(body), kBody[processor_call_count]);
++processor_call_count;
});
auto status = s.PullFrom([&](char *buf, int size) {
return stream.read(buf, size); // The complete chunk is read in one go.
});
EXPECT_EQ(processor_call_count, 2);
}
// Simulate short reads. Each read call only trickles out a few bytes.
TEST(MessageStreamSplitterTest, CompleteReadMultipleMessagesShortRead) {
static constexpr absl::string_view kHeader = "Content-Length: 3\r\n\r\n";
static constexpr absl::string_view kBody[2] = {"foo", "bar"};
static constexpr int kTrickleReadSize = 2;
DataStreamSimulator stream(absl::StrCat(kHeader, kBody[0], kHeader, kBody[1]),
kTrickleReadSize);
MessageStreamSplitter s(4096);
int processor_call_count = 0;
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
EXPECT_EQ(std::string(header), kHeader);
EXPECT_EQ(std::string(body), kBody[processor_call_count]);
++processor_call_count;
});
int read_call_count = 0;
absl::Status status = absl::OkStatus();
while (status.ok()) {
++read_call_count;
status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
}
// Read until we reached EOF, indicated as kUnavailable
EXPECT_EQ(status.code(), absl::StatusCode::kUnavailable); // EOF
EXPECT_GT(read_call_count, 10); // Just checking that it is significantly > 1
EXPECT_EQ(processor_call_count, 2);
}
TEST(MessageStreamSplitterTest, NotAvailableContentHeaderReadError) {
static constexpr absl::string_view kHeader = "not-content-length: 3\r\n\r\n";
static constexpr absl::string_view kBody = "foo";
DataStreamSimulator stream(absl::StrCat(kHeader, kBody));
MessageStreamSplitter s(4096);
int processor_call_count = 0;
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
++processor_call_count;
});
auto status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(), HasSubstr("header"));
EXPECT_EQ(processor_call_count, 0);
}
TEST(MessageStreamSplitterTest, GarbledSizeInContentHeader) {
static constexpr absl::string_view kHeader = "Content-Length: xyz\r\n\r\n";
static constexpr absl::string_view kBody = "foo";
DataStreamSimulator stream(absl::StrCat(kHeader, kBody));
MessageStreamSplitter s(4096);
int processor_call_count = 0;
s.SetMessageProcessor([&](absl::string_view header, absl::string_view body) {
++processor_call_count;
});
auto status =
s.PullFrom([&](char *buf, int size) { return stream.read(buf, size); });
EXPECT_FALSE(status.ok());
EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
EXPECT_THAT(status.message(), HasSubstr("header"));
EXPECT_EQ(processor_call_count, 0);
}
} // namespace lsp
} // namespace verible