1 /*
2  * Copyright 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <list>
20 #include <memory>
21 
22 #include "avrcp_common.h"
23 #include "packet.h"
24 namespace bluetooth {
25 
26 // A helper templated class to access the protected members of Packet to make
27 // testing easier
28 template <class PacketType>
29 class TestPacketType : public PacketType {
30  public:
31   using PacketType::PacketType;
32 
Make()33   static std::shared_ptr<TestPacketType<PacketType>> Make() {
34     return std::shared_ptr<TestPacketType<PacketType>>(
35         new TestPacketType<PacketType>());
36   }
37 
Make(std::shared_ptr<Packet> packet)38   static std::shared_ptr<TestPacketType<PacketType>> Make(
39       std::shared_ptr<Packet> packet) {
40     return std::shared_ptr<TestPacketType<PacketType>>(
41         new TestPacketType<PacketType>(packet));
42   }
43 
Make(std::vector<uint8_t> payload)44   static std::shared_ptr<TestPacketType<PacketType>> Make(
45       std::vector<uint8_t> payload) {
46     size_t end = payload.size();
47     return Make(std::move(payload), 0, end);
48   }
49 
Make(std::vector<uint8_t> payload,size_t start,size_t end)50   static std::shared_ptr<TestPacketType<PacketType>> Make(
51       std::vector<uint8_t> payload, size_t start, size_t end) {
52     auto pkt = std::shared_ptr<TestPacketType<PacketType>>(
53         new TestPacketType<PacketType>());
54     pkt->packet_start_index_ = start;
55     pkt->packet_end_index_ = end;
56     pkt->data_ = std::make_shared<std::vector<uint8_t>>(std::move(payload));
57     return pkt;
58   }
59 
GetData()60   const std::vector<uint8_t>& GetData() { return *PacketType::data_; }
61 
GetDataPointer()62   std::shared_ptr<std::vector<uint8_t>> GetDataPointer() {
63     return PacketType::data_;
64   }
65 };
66 
67 namespace avrcp {
68 
to_string(const Attribute & a)69 inline std::string to_string(const Attribute& a) {
70   switch (a) {
71     case Attribute::TITLE:
72       return "TITLE";
73     case Attribute::ARTIST_NAME:
74       return "ARTIST_NAME";
75     case Attribute::ALBUM_NAME:
76       return "ALBUM_NAME";
77     case Attribute::TRACK_NUMBER:
78       return "TRACK_NUMBER";
79     case Attribute::TOTAL_NUMBER_OF_TRACKS:
80       return "TOTAL_NUMBER_OF_TRACKS";
81     case Attribute::GENRE:
82       return "GENRE";
83     case Attribute::PLAYING_TIME:
84       return "PLAYING_TIME";
85     case Attribute::DEFAULT_COVER_ART:
86       return "DEFAULT_COVER_ART";
87     default:
88       return "UNKNOWN ATTRIBUTE";
89   };
90 }
91 
to_string(const AttributeEntry & entry)92 inline std::string to_string(const AttributeEntry& entry) {
93   std::stringstream ss;
94   ss << to_string(entry.attribute()) << ": " << entry.value();
95   return ss.str();
96 }
97 
98 template <class Container>
to_string(const Container & entries)99 std::string to_string(const Container& entries) {
100   std::stringstream ss;
101   for (const auto& el : entries) {
102     ss << to_string(el) << std::endl;
103   }
104   return ss.str();
105 }
106 
107 inline bool operator==(const AttributeEntry& a, const AttributeEntry& b) {
108   return (a.attribute() == b.attribute()) && (a.value() == b.value());
109 }
110 
111 inline bool operator!=(const AttributeEntry& a, const AttributeEntry& b) {
112   return !(a == b);
113 }
114 
115 template <class AttributesResponseBuilder>
116 class AttributesResponseBuilderTestUser {
117  public:
118   using Builder = AttributesResponseBuilder;
119   using Maker = std::function<typename Builder::Builder(size_t)>;
120 
121  private:
122   Maker maker;
123   typename Builder::Builder _builder;
124   size_t _mtu;
125   size_t _current_size = 0;
126   size_t _entry_counter = 0;
127   std::set<AttributeEntry> _control_set;
128   std::list<AttributeEntry> _order_control;
129   std::list<AttributeEntry> _sended_order;
130   std::stringstream _report;
131   bool _test_result = true;
132   bool _order_test_result = true;
133 
reset()134   void reset() {
135     for (const auto& en : _builder->entries_) {
136       _sended_order.push_back(en);
137     }
138     _current_size = 0, _entry_counter = 0;
139     _control_set.clear();
140     _builder->clear();
141   }
142 
expected_size()143   size_t expected_size() { return Builder::kHeaderSize() + _current_size; }
144 
145  public:
getReport()146   std::string getReport() const { return _report.str(); }
147 
AttributesResponseBuilderTestUser(size_t m_size,Maker maker)148   AttributesResponseBuilderTestUser(size_t m_size, Maker maker)
149       : maker(maker), _builder(maker(m_size)), _mtu(m_size) {
150     _report << __func__ << ": mtu \"" << _mtu << "\"\n";
151   }
152 
startTest(size_t m_size)153   void startTest(size_t m_size) {
154     _builder = maker(m_size);
155     _mtu = m_size;
156     reset();
157     _report.str("");
158     _report.clear();
159     _order_control.clear();
160     _sended_order.clear();
161     _report << __func__ << ": mtu \"" << _mtu << "\"\n";
162     _order_test_result = true;
163     _test_result = true;
164   }
165 
testResult()166   bool testResult() const { return _test_result; }
167 
testOrder()168   bool testOrder() { return _order_test_result; }
169 
finishTest()170   void finishTest() {
171     reset();
172     if (_order_control.size() != _sended_order.size()) {
173       _report << __func__ << ": testOrder FAIL: "
174               << "the count of entries which should send ("
175               << _order_control.size() << ") is not equal to sended entries("
176               << _sended_order.size() << ")) \n input:\n "
177               << to_string(_order_control) << "\n sended:\n"
178               << to_string(_sended_order) << "\n";
179       _order_test_result = false;
180       return;
181     }
182     auto e = _order_control.begin();
183     auto s = _sended_order.begin();
184     for (; e != _order_control.end(); ++e, ++s) {
185       if (*e != *s) {
186         _report << __func__ << "testOrder FAIL: order of entries was changed\n";
187         _order_test_result = false;
188         break;
189       }
190     }
191     _report << __func__ << ": mtu \"" << _mtu << "\"\n";
192   }
193 
AddAttributeEntry(AttributeEntry entry)194   void AddAttributeEntry(AttributeEntry entry) {
195     auto f = _builder->AddAttributeEntry(entry);
196     if (f != 0) {
197       _current_size += f;
198       ++_entry_counter;
199     }
200     if (f == entry.size()) {
201       wholeEntry(f, std::move(entry));
202     } else {
203       fractionEntry(f, std::move(entry));
204     }
205   }
206 
207  private:
wholeEntry(size_t f,AttributeEntry && entry)208   void wholeEntry(size_t f, AttributeEntry&& entry) {
209     _control_set.insert(entry);
210     _order_control.push_back(entry);
211     if (_builder->size() != expected_size()) {
212       _report << __func__ << "FAIL for \"" << to_string(entry)
213               << "\": not allowed to add.\n";
214       _test_result = false;
215     }
216   }
217 
fractionEntry(size_t f,AttributeEntry && entry)218   void fractionEntry(size_t f, AttributeEntry&& entry) {
219     auto l_value = entry.value().size() - (entry.size() - f);
220     if (f != 0) {
221       auto pushed_entry = AttributeEntry(
222           entry.attribute(), std::string(entry.value(), 0, l_value));
223       _control_set.insert(pushed_entry);
224       _order_control.push_back(pushed_entry);
225     }
226 
227     if (expected_size() != _builder->size()) {
228       _test_result = false;
229       _report << __func__ << "FAIL for \"" << to_string(entry)
230               << "\": not allowed to add.\n";
231     }
232 
233     if (_builder->size() != expected_size() ||
234         _builder->entries_.size() != _entry_counter) {
235       _report << __func__ << "FAIL for \"" << to_string(entry)
236               << "\": unexpected size of packet\n";
237       _test_result = false;
238     }
239     for (auto dat = _builder->entries_.begin(), ex = _control_set.begin();
240          ex != _control_set.end(); ++dat, ++ex) {
241       if (*dat != *ex) {
242         _report << __func__ << "FAIL for \"" << to_string(entry)
243                 << "\": unexpected entry order\n";
244         _test_result = false;
245       }
246     }
247     auto tail = (f == 0) ? entry
248                          : AttributeEntry(entry.attribute(),
249                                           std::string(entry.value(), l_value));
250     if (_builder->entries_.size() != 0) {
251       reset();
252       AddAttributeEntry(tail);
253     }
254     if (_builder->entries_.size() == 0) {
255       _report << __func__ << "FAIL: MTU " << _mtu << " too small\n";
256       _test_result = false;
257       _order_control.push_back(entry);
258       reset();
259     }
260   }
261 };
262 
263 template <class AttributesBuilder>
264 class FragmentationBuilderHelper {
265  public:
266   using Builder = AttributesBuilder;
267   using Helper = AttributesResponseBuilderTestUser<Builder>;
268   using Maker = typename Helper::Maker;
269 
FragmentationBuilderHelper(size_t mtu,Maker m)270   FragmentationBuilderHelper(size_t mtu, Maker m) : _helper(mtu, m) {}
271 
272   template <class TestCollection>
273   void runTest(const TestCollection& test_data, size_t mtu,
274                bool expect_fragmentation = true, bool expect_ordering = true) {
275     _helper.startTest(mtu);
276 
277     for (auto& i : test_data) {
278       _helper.AddAttributeEntry(i);
279     }
280     _helper.finishTest();
281 
282     EXPECT_EQ(expect_fragmentation, _helper.testResult())
283         << "Report: " << _helper.getReport();
284     EXPECT_EQ(expect_ordering, _helper.testOrder())
285         << "Report: " << _helper.getReport();
286   }
287 
288  private:
289   Helper _helper;
290 };
291 }  // namespace avrcp
292 }  // namespace bluetooth
293