android13/packages/modules/Bluetooth/system/packet/tests/packet_test_helper.h

293 lines
8.6 KiB
C++

/*
* Copyright 2018 The Android Open Source Project
*
* 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.
*/
#pragma once
#include <list>
#include <memory>
#include "avrcp_common.h"
#include "packet.h"
namespace bluetooth {
// A helper templated class to access the protected members of Packet to make
// testing easier
template <class PacketType>
class TestPacketType : public PacketType {
public:
using PacketType::PacketType;
static std::shared_ptr<TestPacketType<PacketType>> Make() {
return std::shared_ptr<TestPacketType<PacketType>>(
new TestPacketType<PacketType>());
}
static std::shared_ptr<TestPacketType<PacketType>> Make(
std::shared_ptr<Packet> packet) {
return std::shared_ptr<TestPacketType<PacketType>>(
new TestPacketType<PacketType>(packet));
}
static std::shared_ptr<TestPacketType<PacketType>> Make(
std::vector<uint8_t> payload) {
size_t end = payload.size();
return Make(std::move(payload), 0, end);
}
static std::shared_ptr<TestPacketType<PacketType>> Make(
std::vector<uint8_t> payload, size_t start, size_t end) {
auto pkt = std::shared_ptr<TestPacketType<PacketType>>(
new TestPacketType<PacketType>());
pkt->packet_start_index_ = start;
pkt->packet_end_index_ = end;
pkt->data_ = std::make_shared<std::vector<uint8_t>>(std::move(payload));
return pkt;
}
const std::vector<uint8_t>& GetData() { return *PacketType::data_; }
std::shared_ptr<std::vector<uint8_t>> GetDataPointer() {
return PacketType::data_;
}
};
namespace avrcp {
inline std::string to_string(const Attribute& a) {
switch (a) {
case Attribute::TITLE:
return "TITLE";
case Attribute::ARTIST_NAME:
return "ARTIST_NAME";
case Attribute::ALBUM_NAME:
return "ALBUM_NAME";
case Attribute::TRACK_NUMBER:
return "TRACK_NUMBER";
case Attribute::TOTAL_NUMBER_OF_TRACKS:
return "TOTAL_NUMBER_OF_TRACKS";
case Attribute::GENRE:
return "GENRE";
case Attribute::PLAYING_TIME:
return "PLAYING_TIME";
case Attribute::DEFAULT_COVER_ART:
return "DEFAULT_COVER_ART";
default:
return "UNKNOWN ATTRIBUTE";
};
}
inline std::string to_string(const AttributeEntry& entry) {
std::stringstream ss;
ss << to_string(entry.attribute()) << ": " << entry.value();
return ss.str();
}
template <class Container>
std::string to_string(const Container& entries) {
std::stringstream ss;
for (const auto& el : entries) {
ss << to_string(el) << std::endl;
}
return ss.str();
}
inline bool operator==(const AttributeEntry& a, const AttributeEntry& b) {
return (a.attribute() == b.attribute()) && (a.value() == b.value());
}
inline bool operator!=(const AttributeEntry& a, const AttributeEntry& b) {
return !(a == b);
}
template <class AttributesResponseBuilder>
class AttributesResponseBuilderTestUser {
public:
using Builder = AttributesResponseBuilder;
using Maker = std::function<typename Builder::Builder(size_t)>;
private:
Maker maker;
typename Builder::Builder _builder;
size_t _mtu;
size_t _current_size = 0;
size_t _entry_counter = 0;
std::set<AttributeEntry> _control_set;
std::list<AttributeEntry> _order_control;
std::list<AttributeEntry> _sended_order;
std::stringstream _report;
bool _test_result = true;
bool _order_test_result = true;
void reset() {
for (const auto& en : _builder->entries_) {
_sended_order.push_back(en);
}
_current_size = 0, _entry_counter = 0;
_control_set.clear();
_builder->clear();
}
size_t expected_size() { return Builder::kHeaderSize() + _current_size; }
public:
std::string getReport() const { return _report.str(); }
AttributesResponseBuilderTestUser(size_t m_size, Maker maker)
: maker(maker), _builder(maker(m_size)), _mtu(m_size) {
_report << __func__ << ": mtu \"" << _mtu << "\"\n";
}
void startTest(size_t m_size) {
_builder = maker(m_size);
_mtu = m_size;
reset();
_report.str("");
_report.clear();
_order_control.clear();
_sended_order.clear();
_report << __func__ << ": mtu \"" << _mtu << "\"\n";
_order_test_result = true;
_test_result = true;
}
bool testResult() const { return _test_result; }
bool testOrder() { return _order_test_result; }
void finishTest() {
reset();
if (_order_control.size() != _sended_order.size()) {
_report << __func__ << ": testOrder FAIL: "
<< "the count of entries which should send ("
<< _order_control.size() << ") is not equal to sended entries("
<< _sended_order.size() << ")) \n input:\n "
<< to_string(_order_control) << "\n sended:\n"
<< to_string(_sended_order) << "\n";
_order_test_result = false;
return;
}
auto e = _order_control.begin();
auto s = _sended_order.begin();
for (; e != _order_control.end(); ++e, ++s) {
if (*e != *s) {
_report << __func__ << "testOrder FAIL: order of entries was changed\n";
_order_test_result = false;
break;
}
}
_report << __func__ << ": mtu \"" << _mtu << "\"\n";
}
void AddAttributeEntry(AttributeEntry entry) {
auto f = _builder->AddAttributeEntry(entry);
if (f != 0) {
_current_size += f;
++_entry_counter;
}
if (f == entry.size()) {
wholeEntry(f, std::move(entry));
} else {
fractionEntry(f, std::move(entry));
}
}
private:
void wholeEntry(size_t f, AttributeEntry&& entry) {
_control_set.insert(entry);
_order_control.push_back(entry);
if (_builder->size() != expected_size()) {
_report << __func__ << "FAIL for \"" << to_string(entry)
<< "\": not allowed to add.\n";
_test_result = false;
}
}
void fractionEntry(size_t f, AttributeEntry&& entry) {
auto l_value = entry.value().size() - (entry.size() - f);
if (f != 0) {
auto pushed_entry = AttributeEntry(
entry.attribute(), std::string(entry.value(), 0, l_value));
_control_set.insert(pushed_entry);
_order_control.push_back(pushed_entry);
}
if (expected_size() != _builder->size()) {
_test_result = false;
_report << __func__ << "FAIL for \"" << to_string(entry)
<< "\": not allowed to add.\n";
}
if (_builder->size() != expected_size() ||
_builder->entries_.size() != _entry_counter) {
_report << __func__ << "FAIL for \"" << to_string(entry)
<< "\": unexpected size of packet\n";
_test_result = false;
}
for (auto dat = _builder->entries_.begin(), ex = _control_set.begin();
ex != _control_set.end(); ++dat, ++ex) {
if (*dat != *ex) {
_report << __func__ << "FAIL for \"" << to_string(entry)
<< "\": unexpected entry order\n";
_test_result = false;
}
}
auto tail = (f == 0) ? entry
: AttributeEntry(entry.attribute(),
std::string(entry.value(), l_value));
if (_builder->entries_.size() != 0) {
reset();
AddAttributeEntry(tail);
}
if (_builder->entries_.size() == 0) {
_report << __func__ << "FAIL: MTU " << _mtu << " too small\n";
_test_result = false;
_order_control.push_back(entry);
reset();
}
}
};
template <class AttributesBuilder>
class FragmentationBuilderHelper {
public:
using Builder = AttributesBuilder;
using Helper = AttributesResponseBuilderTestUser<Builder>;
using Maker = typename Helper::Maker;
FragmentationBuilderHelper(size_t mtu, Maker m) : _helper(mtu, m) {}
template <class TestCollection>
void runTest(const TestCollection& test_data, size_t mtu,
bool expect_fragmentation = true, bool expect_ordering = true) {
_helper.startTest(mtu);
for (auto& i : test_data) {
_helper.AddAttributeEntry(i);
}
_helper.finishTest();
EXPECT_EQ(expect_fragmentation, _helper.testResult())
<< "Report: " << _helper.getReport();
EXPECT_EQ(expect_ordering, _helper.testOrder())
<< "Report: " << _helper.getReport();
}
private:
Helper _helper;
};
} // namespace avrcp
} // namespace bluetooth