blob: 8ac4367c2b4099b842a23acab633334786a53422 [file] [log] [blame]
// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "receive_mode.h"
#include "gtest/gtest.h"
#include "receive_mode_fuzzer_helpers.h"
namespace overnet {
namespace receive_mode {
namespace receive_mode_test {
// Wrapper to allow testing parameterized modes without needing to add
// constructor variants everywhere
template <ReliabilityAndOrdering reliability_and_ordering>
class ParameterizedWrapper final : public ReceiveMode {
public:
ParameterizedWrapper() : mode_(reliability_and_ordering) {}
void Begin(uint64_t seq, BeginCallback ready) override {
mode_.Begin(seq, std::move(ready));
}
void Completed(uint64_t seq, const Status& status) override {
mode_.Completed(seq, status);
}
void Close(const Status& status) override { mode_.Close(status); }
private:
ParameterizedReceiveMode mode_;
};
template <class Type>
class ReceiveModeTest : public ::testing::Test {
public:
void Begin(uint64_t seq) {
auto cb = StatusCallback(ALLOCATED_CALLBACK, [=](const Status& status) {
auto it = expected_begin_cbs_.find(seq);
if (it == expected_begin_cbs_.end()) {
FAIL() << "Unexpected Begin callback: seq=" << seq
<< " status=" << status;
}
if (status.code() != it->second) {
FAIL() << "Unexpected status for Begin callback: seq=" << seq
<< " expected " << StatusCodeString(it->second) << " but got "
<< status;
}
expected_begin_cbs_.erase(it);
});
type_.Begin(seq, std::move(cb));
}
void ExpectBegin(uint64_t seq, StatusCode expect) {
EXPECT_EQ(0u, expected_begin_cbs_.count(seq));
expected_begin_cbs_[seq] = expect;
}
void Completed(uint64_t seq, const Status& status) {
type_.Completed(seq, status);
}
~ReceiveModeTest() {
for (auto exp : expected_begin_cbs_) {
ADD_FAILURE() << "Expected seq " << exp.first
<< " to complete with status "
<< StatusCodeString(exp.second) << " but got nothing";
}
}
private:
Type type_;
std::unordered_map<uint64_t, StatusCode> expected_begin_cbs_;
};
///////////////////////////////////////////////////////////////////////////////
// Generic tests
typedef ::testing::Types<
ReliableOrdered, ReliableUnordered, UnreliableOrdered, UnreliableUnordered,
TailReliable, ParameterizedWrapper<ReliabilityAndOrdering::ReliableOrdered>,
ParameterizedWrapper<ReliabilityAndOrdering::ReliableUnordered>,
ParameterizedWrapper<ReliabilityAndOrdering::UnreliableOrdered>,
ParameterizedWrapper<ReliabilityAndOrdering::UnreliableUnordered>,
ParameterizedWrapper<ReliabilityAndOrdering::TailReliable>>
ReceiveModeTypes;
TYPED_TEST_CASE(ReceiveModeTest, ReceiveModeTypes);
TYPED_TEST(ReceiveModeTest, SimpleInSeq) {
for (uint64_t i = 1u; i <= 10u; i++) {
this->ExpectBegin(i, StatusCode::OK);
this->Begin(i);
this->Completed(i, Status::Ok());
}
}
TYPED_TEST(ReceiveModeTest, SmallBatchInSeq) {
for (uint64_t i = 1u; i <= 10u; i++) {
this->ExpectBegin(i, StatusCode::OK);
}
for (uint64_t i = 1u; i <= 10u; i++) {
this->Begin(i);
}
for (uint64_t i = 1u; i <= 10u; i++) {
this->Completed(i, Status::Ok());
}
}
TYPED_TEST(ReceiveModeTest, RepeatedFailure) {
for (uint64_t i = 1u; i <= 10u; i++) {
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
this->Completed(1, Status::Cancelled());
}
}
TYPED_TEST(ReceiveModeTest, DuplicateBegin) {
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
// get dup while processing
this->ExpectBegin(1, StatusCode::CANCELLED);
this->Begin(1);
this->Completed(1, StatusCode::OK);
// get dup after processing
this->ExpectBegin(1, StatusCode::CANCELLED);
this->Begin(1);
}
///////////////////////////////////////////////////////////////////////////////
// ReliableOrdered specific tests
template <class T>
using ReliableOrderedTest = ReceiveModeTest<T>;
typedef ::testing::Types<
ReliableOrdered,
ParameterizedWrapper<ReliabilityAndOrdering::ReliableOrdered>>
ReliableOrderedTypes;
TYPED_TEST_CASE(ReliableOrderedTest, ReliableOrderedTypes);
TYPED_TEST(ReliableOrderedTest, MissedOneThenGotIt) {
this->Begin(2);
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
this->ExpectBegin(2, StatusCode::OK);
this->Completed(1, Status::Ok());
this->Completed(2, Status::Ok());
}
///////////////////////////////////////////////////////////////////////////////
// ReliableUnordered specific tests
template <class T>
using ReliableUnorderedTest = ReceiveModeTest<T>;
typedef ::testing::Types<
ReliableUnordered,
ParameterizedWrapper<ReliabilityAndOrdering::ReliableUnordered>>
ReliableUnorderedTypes;
TYPED_TEST_CASE(ReliableUnorderedTest, ReliableUnorderedTypes);
TYPED_TEST(ReliableUnorderedTest, MissedOneThenGotIt) {
this->ExpectBegin(2, StatusCode::OK);
this->Begin(2);
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
this->Completed(1, Status::Ok());
this->Completed(2, Status::Ok());
}
///////////////////////////////////////////////////////////////////////////////
// UnreliableOrdered specific tests
template <class T>
using UnreliableOrderedTest = ReceiveModeTest<T>;
typedef ::testing::Types<
UnreliableOrdered,
ParameterizedWrapper<ReliabilityAndOrdering::UnreliableOrdered>>
UnreliableOrderedTypes;
TYPED_TEST_CASE(UnreliableOrderedTest, UnreliableOrderedTypes);
TYPED_TEST(UnreliableOrderedTest, Skippy) {
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
this->Completed(1, Status::Ok());
this->ExpectBegin(2, StatusCode::OK);
this->Begin(2);
this->Begin(4);
this->ExpectBegin(4, StatusCode::OK);
this->Completed(2, Status::Ok());
this->ExpectBegin(3, StatusCode::CANCELLED);
this->Begin(3);
this->Begin(70000000);
this->ExpectBegin(70000000, StatusCode::OK);
this->Completed(4, StatusCode::CANCELLED);
this->Completed(70000000, Status::Ok());
}
///////////////////////////////////////////////////////////////////////////////
// UnreliableUnordered specific tests
template <class T>
using UnreliableUnorderedTest = ReceiveModeTest<T>;
typedef ::testing::Types<
UnreliableUnordered,
ParameterizedWrapper<ReliabilityAndOrdering::UnreliableUnordered>>
UnreliableUnorderedTypes;
TYPED_TEST_CASE(UnreliableUnorderedTest, UnreliableUnorderedTypes);
TYPED_TEST(UnreliableUnorderedTest, AnythingGoesReally) {
this->ExpectBegin(3, StatusCode::OK);
this->Begin(3);
this->ExpectBegin(2, StatusCode::OK);
this->Begin(2);
this->Completed(2, Status::Ok());
this->Completed(3, Status::Cancelled());
this->ExpectBegin(1, StatusCode::OK);
this->Begin(1);
this->Completed(1, Status::Cancelled());
}
///////////////////////////////////////////////////////////////////////////////
// Error specific tests
template <class T>
using ErrorTest = ReceiveModeTest<T>;
typedef ::testing::Types<
Error, ParameterizedWrapper<static_cast<ReliabilityAndOrdering>(255)>>
ErrorTypes;
TYPED_TEST_CASE(ErrorTest, ErrorTypes);
TYPED_TEST(ErrorTest, BeginAlwaysFails) {
for (uint64_t i = 0; i < 20; i++) {
this->ExpectBegin(i, StatusCode::CANCELLED);
this->Begin(i);
}
}
///////////////////////////////////////////////////////////////////////////////
// Fuzzer found failures
//
// Procedure: run the receive_mode_fuzzer
// use ./receive_mode_fuzzer_corpus_to_code.py corpus_entry
// copy paste test here, leave a comment about what failed
// (also, fix it)
// Originally led to an OOM generating nacks
TEST(ReceiveModeFuzzed, _18dd797d7734fe115b89e72e79a26713c480096c) {
receive_mode::ParameterizedReceiveMode m(
static_cast<ReliabilityAndOrdering>(1));
m.Begin(112ull, StatusCallback([](const Status&) {}));
m.Begin(1125899905794047ull, StatusCallback([](const Status&) {}));
}
// Originally led to an out of range index into a bitset
TEST(ReceiveModeFuzzed, _0ea3e010446b0d18d1a08efc4e7f028372140b81) {
receive_mode::ParameterizedReceiveMode m(
static_cast<ReliabilityAndOrdering>(3));
m.Begin(450ull, StatusCallback([](const Status&) {}));
}
// Originally led to an OOM generating nacks
TEST(ReceiveModeFuzzed, _feac8b7a55c39b0d70f86f40d340888a25ea69b8) {
receive_mode::ParameterizedReceiveMode m(
static_cast<ReliabilityAndOrdering>(4));
m.Begin(1ull, StatusCallback([](const Status&) {}));
m.Begin(143547839805374333ull, StatusCallback([](const Status&) {}));
m.Begin(139044205818265471ull, StatusCallback([](const Status&) {}));
m.Begin(0ull, StatusCallback([](const Status&) {}));
}
// Originally led to a crash
TEST(ReceiveModeFuzzed, _75079e04acd10ecdf49c97c714f344c811e870dd) {
receive_mode::Fuzzer m(3);
m.Step();
if (!m.Begin(3ull)) return;
m.Step();
if (!m.Begin(33ull)) return;
m.Step();
if (!m.Begin(255ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(89ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(2ull)) return;
m.Step();
if (!m.Completed(1ull, 0)) return;
m.Step();
if (!m.Begin(112ull)) return;
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
if (!m.Begin(3ull)) return;
m.Step();
if (!m.Begin(3ull)) return;
m.Step();
if (!m.Begin(33ull)) return; // Crash occurred here, rest of test clipped.
}
TEST(ReceiveModeFuzzed, _6b1d71bc2330430a1719c9774a7408bbd3aa7f29) {
receive_mode::Fuzzer m(2);
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Completed(1ull, 1)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(2ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(3ull)) return;
m.Step();
if (!m.Begin(3ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(112ull)) return;
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Completed(1ull, 0)) return;
m.Step();
if (!m.Begin(30331092862ull)) return;
m.Step();
if (!m.Begin(68ull)) return;
m.Step();
if (!m.Begin(10737418235ull)) return;
m.Step();
if (!m.Begin(112ull)) return;
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
if (!m.Begin(34357641211ull)) return;
m.Step();
if (!m.Begin(268435451ull)) return;
m.Step();
if (!m.Begin(10737418235ull)) return;
m.Step();
if (!m.Begin(112ull)) return; // Crash occurred here, rest of test clipped.
}
TEST(ReceiveModeFuzzed, _d7c39af715153bc0ca5dcfd96c38a0774f73967b) {
receive_mode::Fuzzer m(3);
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
if (!m.Begin(18446744073709551615ull)) return;
m.Step();
}
// Discovered a uint64 overflow bug
TEST(ReceiveModeFuzzed, _b26f6446c4a821539edb0e106c30f50bff6ad176) {
receive_mode::Fuzzer m(3);
m.Step();
if (!m.Begin(64ull)) return;
m.Step();
if (!m.Begin(40ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(41ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
if (!m.Begin(496667600076670ull)) return;
m.Step();
if (!m.Begin(68ull)) return;
m.Step();
if (!m.Begin(8796084633595ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(9ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(139044205818265471ull)) return;
m.Step();
if (!m.Begin(18446744073709551615ull)) return;
m.Step();
if (!m.Begin(229ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(16383ull)) return;
m.Step();
if (!m.Begin(4088538316667ull)) return;
m.Step();
if (!m.Begin(246ull)) return;
m.Step();
if (!m.Begin(18446744073709551615ull)) return;
m.Step();
if (!m.Begin(229ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(1ull)) return;
m.Step();
if (!m.Begin(16383ull)) return;
m.Step();
if (!m.Begin(562949408145275ull)) return;
m.Step();
if (!m.Begin(10737418235ull)) return;
m.Step();
if (!m.Begin(112ull)) return;
m.Step();
if (!m.Begin(0ull)) return;
m.Step();
}
TEST(ReceiveModeFuzzed, _c5e4ebd8acd50abb27258f73956647303d781a45) {
receive_mode::Fuzzer m(1);
m.Step();
if (!m.Begin(18446744073709551615ull)) return;
m.Step();
if (!m.Completed(18446744073709551615ull, 0)) return;
m.Step();
}
} // namespace receive_mode_test
} // namespace receive_mode
} // namespace overnet