DCCL v5
Loading...
Searching...
No Matches
test.cpp
1// Copyright 2024:
2// GobySoft, LLC (2013-)
3// Community contributors (see AUTHORS file)
4// File authors:
5// Toby Schneider <toby@gobysoft.org>
6//
7//
8// This file is part of the Dynamic Compact Control Language Library
9// ("DCCL").
10//
11// DCCL is free software: you can redistribute it and/or modify
12// it under the terms of the GNU Lesser General Public License as published by
13// the Free Software Foundation, either version 2.1 of the License, or
14// (at your option) any later version.
15//
16// DCCL is distributed in the hope that it will be useful,
17// but WITHOUT ANY WARRANTY; without even the implied warranty of
18// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19// GNU Lesser General Public License for more details.
20//
21// You should have received a copy of the GNU Lesser General Public License
22// along with DCCL. If not, see <http://www.gnu.org/licenses/>.
23// tests CRC field codecs (dccl.crc16 and dccl.crc32)
24
25#include <cassert>
26#include <iostream>
27#include <stdexcept>
28
29#include "../../binary.h"
30#include "../../codec.h"
31#include "test.pb.h"
32
33using namespace dccl::test;
34
35int main(int argc, char* argv[])
36{
37 bool verbose = false;
38 for (int i = 1; i < argc; ++i)
39 {
40 if (argv[i] && argv[i][0] == '-' && argv[i][1] == 'v' && argv[i][2] == '\0')
41 verbose = true;
42 }
43
44 dccl::dlog.connect(verbose ? dccl::logger::ALL : dccl::logger::WARN_PLUS, &std::cerr);
45
46 dccl::Codec codec;
47
48 //
49 // Test CRC-16
50 //
51 {
52 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "=== Testing CRC-16 ===" << std::endl;
53 codec.load<TestCRC16>();
54 if (dccl::dlog.is(dccl::logger::INFO))
55 {
56 codec.info<TestCRC16>(&dccl::dlog);
57 }
58
59 TestCRC16 msg_in, msg_out;
60 msg_in.set_x(1234);
61 msg_in.set_y(-5678);
62 msg_in.set_crc(0); // dummy value - overwritten by dccl.crc16 codec
63
64 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Message in:\n"
65 << msg_in.DebugString() << std::endl;
66 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Encoding..." << std::endl;
67 std::string bytes;
68 codec.encode(&bytes, msg_in);
69 dccl::dlog.is(dccl::logger::INFO) &&
70 dccl::dlog << "Encoded (hex): " << dccl::hex_encode(bytes) << std::endl;
71
72 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Decoding..." << std::endl;
73 try
74 {
75 codec.decode(bytes, &msg_out);
76 }
77 catch (const std::exception& e)
78 {
79 std::cerr << "UNEXPECTED exception during decode: " << e.what() << std::endl;
80 assert(false);
81 }
82 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Message out:\n"
83 << msg_out.DebugString() << std::endl;
84
85 assert(msg_out.x() == msg_in.x());
86 assert(msg_out.y() == msg_in.y());
87 assert(msg_out.crc() != 0); // CRC should be set
88
89 // Verify that the CRC in the decoded message matches what was encoded
90 assert(msg_out.crc() == msg_out.crc());
91
92 // Test that a corrupt message throws an exception
93 std::string corrupt_bytes = bytes;
94 corrupt_bytes[corrupt_bytes.size() - 1] ^= 0xFF; // flip bits in last byte
95 try
96 {
97 TestCRC16 corrupt_out;
98 codec.decode(corrupt_bytes, &corrupt_out);
99 std::cerr << "ERROR: Corrupt message did not throw exception!" << std::endl;
100 assert(false);
101 }
102 catch (const std::exception& e)
103 {
104 dccl::dlog.is(dccl::logger::INFO) &&
105 dccl::dlog << "Caught expected exception for corrupt CRC-16: " << e.what()
106 << std::endl;
107 }
108
109 codec.unload<TestCRC16>();
110 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "CRC-16 tests passed!" << std::endl;
111 }
112
113 //
114 // Test CRC-32
115 //
116 {
117 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "\n=== Testing CRC-32 ===" << std::endl;
118 codec.load<TestCRC32>();
119 if (dccl::dlog.is(dccl::logger::INFO))
120 {
121 codec.info<TestCRC32>(&dccl::dlog);
122 }
123
124 TestCRC32 msg_in, msg_out;
125 msg_in.set_x(9999);
126 msg_in.set_y(-9999);
127 msg_in.set_crc(0); // dummy value - overwritten by dccl.crc32 codec
128
129 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Message in:\n"
130 << msg_in.DebugString() << std::endl;
131 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Encoding..." << std::endl;
132 std::string bytes;
133 codec.encode(&bytes, msg_in);
134 dccl::dlog.is(dccl::logger::INFO) &&
135 dccl::dlog << "Encoded (hex): " << dccl::hex_encode(bytes) << std::endl;
136
137 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Decoding..." << std::endl;
138 codec.decode(bytes, &msg_out);
139 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Message out:\n"
140 << msg_out.DebugString() << std::endl;
141
142 assert(msg_out.x() == msg_in.x());
143 assert(msg_out.y() == msg_in.y());
144 assert(msg_out.crc() != 0); // CRC should be set
145
146 // Test that a corrupt message throws an exception
147 std::string corrupt_bytes = bytes;
148 corrupt_bytes[corrupt_bytes.size() - 1] ^= 0xFF; // flip bits in last byte
149 try
150 {
151 TestCRC32 corrupt_out;
152 codec.decode(corrupt_bytes, &corrupt_out);
153 std::cerr << "ERROR: Corrupt message did not throw exception!" << std::endl;
154 assert(false);
155 }
156 catch (const std::exception& e)
157 {
158 dccl::dlog.is(dccl::logger::INFO) &&
159 dccl::dlog << "Caught expected exception for corrupt CRC-32: " << e.what()
160 << std::endl;
161 }
162
163 codec.unload<TestCRC32>();
164 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "CRC-32 tests passed!" << std::endl;
165 }
166
167 //
168 // Test encoding twice gives same result (CRC is deterministic)
169 //
170 {
171 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "\n=== Testing CRC-16 determinism ==="
172 << std::endl;
173 codec.load<TestCRC16>();
174
175 TestCRC16 msg;
176 msg.set_x(42);
177 msg.set_y(100);
178 msg.set_crc(0);
179
180 std::string bytes1, bytes2;
181 codec.encode(&bytes1, msg);
182 codec.encode(&bytes2, msg);
183 assert(bytes1 == bytes2);
184 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "CRC is deterministic." << std::endl;
185
186 // Test different field values give different CRC
187 TestCRC16 msg2;
188 msg2.set_x(43); // different x
189 msg2.set_y(100);
190 msg2.set_crc(0);
191 std::string bytes3;
192 codec.encode(&bytes3, msg2);
193 assert(bytes3 != bytes1);
194 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "CRC differs for different data."
195 << std::endl;
196
197 codec.unload<TestCRC16>();
198 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Determinism tests passed!" << std::endl;
199 }
200
201 //
202 // Test CRC-32 over optional, variable length, repeated and embedded fields
203 //
204 {
205 dccl::dlog.is(dccl::logger::INFO) &&
206 dccl::dlog << "\n=== Testing CRC-32 over all field types ===" << std::endl;
207 codec.load<TestCRCAllFields>();
208
209 // once with every optional field set, once with all of them omitted
210 for (int all_set = 1; all_set >= 0; --all_set)
211 {
212 TestCRCAllFields msg_in, msg_out;
213 msg_in.set_x(1234);
214 msg_in.mutable_em()->set_a(7);
215 if (all_set)
216 {
217 msg_in.set_h(42);
218 msg_in.set_y(-4321);
219 msg_in.set_s("hello");
220 msg_in.add_r(1);
221 msg_in.add_r(2);
222 msg_in.mutable_em()->set_b("world");
223 msg_in.mutable_em_opt()->set_a(-7);
224 }
225
226 msg_in.set_crc(0); // dummy value - overwritten by the dccl.crc32 codec
227
228 std::string bytes;
229 codec.encode(&bytes, msg_in);
230 dccl::dlog.is(dccl::logger::INFO) &&
231 dccl::dlog << "Encoded (hex): " << dccl::hex_encode(bytes) << std::endl;
232 codec.decode(bytes, &msg_out);
233 assert(msg_out.crc() != 0);
234 msg_out.set_crc(0); // set on encoding, so not part of the round trip
235 assert(msg_out.DebugString() == msg_in.DebugString());
236
237 // corruption anywhere in the body (all the CRC covers) must still be caught
238 std::string head;
239 codec.encode(&head, msg_in, true);
240 for (std::string::size_type i = head.size(); i < bytes.size(); ++i)
241 {
242 std::string corrupt_bytes = bytes;
243 corrupt_bytes[i] ^= 0x01;
244 if (corrupt_bytes == bytes)
245 continue;
246
247 try
248 {
249 TestCRCAllFields corrupt_out;
250 codec.decode(corrupt_bytes, &corrupt_out);
251 std::cerr << "ERROR: corrupt message (byte " << i << ") did not throw!"
252 << std::endl;
253 assert(false);
254 }
255 catch (const std::exception& e)
256 {
257 dccl::dlog.is(dccl::logger::INFO) &&
258 dccl::dlog << "Caught expected exception: " << e.what() << std::endl;
259 }
260 }
261 }
262
263 codec.unload<TestCRCAllFields>();
264 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "All field type tests passed!"
265 << std::endl;
266 }
267
268 //
269 // Test CRC-32 over a oneof
270 //
271 {
272 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "\n=== Testing CRC-32 over a oneof ==="
273 << std::endl;
274 codec.load<TestCRCOneof>();
275
276 TestCRCOneof msg_in, msg_out;
277 msg_in.set_y(-4321);
278 msg_in.set_crc(0);
279
280 std::string bytes;
281 codec.encode(&bytes, msg_in);
282 codec.decode(bytes, &msg_out);
283 assert(msg_out.y() == msg_in.y());
284 assert(!msg_out.has_x());
285
286 codec.unload<TestCRCOneof>();
287 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "Oneof tests passed!" << std::endl;
288 }
289
290 //
291 // A CRC in an embedded message cannot be verified, so it must not load
292 //
293 {
294 dccl::dlog.is(dccl::logger::INFO) &&
295 dccl::dlog << "\n=== Testing CRC in an embedded message ===" << std::endl;
296 try
297 {
298 codec.load<TestCRCBadNested>();
299 std::cerr << "ERROR: CRC in an embedded message loaded!" << std::endl;
300 assert(false);
301 }
302 catch (const std::exception& e)
303 {
304 dccl::dlog.is(dccl::logger::INFO) &&
305 dccl::dlog << "Caught expected exception: " << e.what() << std::endl;
306 }
307 }
308
309 dccl::dlog.is(dccl::logger::INFO) && dccl::dlog << "\nAll CRC tests passed!" << std::endl;
310 return 0;
311}
The Dynamic CCL enCODer/DECoder. This is the main class you will use to load, encode and decode DCCL ...
Definition codec.h:61
bool is(logger::Verbosity verbosity, logger::Group group=logger::GENERAL)
Indicates the verbosity of the Logger until the next std::flush or std::endl. The boolean return is u...
Definition logger.h:191
void connect(int verbosity_mask, Slot slot)
Connect the output of one or more given verbosities to a slot (function pointer or similar)
Definition logger.h:213
Unit test namespace.
Definition test.cpp:45
Dynamic Compact Control Language namespace.
Definition any.h:28
void hex_encode(CharIterator begin, CharIterator end, std::string *out, bool upper_case=false)
Encodes a (little-endian) hexadecimal string from a byte string. Index 0 of begin is written to index...
Definition binary.h:95