Fix for x86_64 build fail
[platform/upstream/connectedhomeip.git] / src / setup_payload / tests / TestQRCode.cpp
1 /*
2  *
3  *    Copyright (c) 2020 Project CHIP Authors
4  *
5  *    Licensed under the Apache License, Version 2.0 (the "License");
6  *    you may not use this file except in compliance with the License.
7  *    You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *    Unless required by applicable law or agreed to in writing, software
12  *    distributed under the License is distributed on an "AS IS" BASIS,
13  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *    See the License for the specific language governing permissions and
15  *    limitations under the License.
16  */
17
18 /**
19  *    @file
20  *      This file implements a unit test suite for the Quick Response
21  *      code functionality.
22  *
23  */
24
25 #include "TestHelpers.h"
26
27 #include <nlbyteorder.h>
28 #include <nlunit-test.h>
29
30 #include <support/UnitTestRegistration.h>
31
32 using namespace chip;
33 using namespace std;
34
35 namespace {
36
37 void TestRendezvousFlags(nlTestSuite * inSuite, void * inContext)
38 {
39     SetupPayload inPayload = GetDefaultPayload();
40
41     inPayload.rendezvousInformation = RendezvousInformationFlags::kNone;
42     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
43
44     inPayload.rendezvousInformation = RendezvousInformationFlags::kWiFi;
45     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
46
47     inPayload.rendezvousInformation = RendezvousInformationFlags::kBLE;
48     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
49
50     inPayload.rendezvousInformation = RendezvousInformationFlags::kThread;
51     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
52
53     inPayload.rendezvousInformation = RendezvousInformationFlags::kEthernet;
54     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
55
56     inPayload.rendezvousInformation = RendezvousInformationFlags::kAllMask;
57     NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
58 }
59
60 void TestPayloadByteArrayRep(nlTestSuite * inSuite, void * inContext)
61 {
62     SetupPayload payload = GetDefaultPayload();
63
64     string expected = " 00000 000000000000000100000000000 000010000000 00000001 0 0000000000000001 0000000000001100 101";
65     NL_TEST_ASSERT(inSuite, CompareBinary(payload, expected));
66 }
67
68 void TestPayloadBase41Rep(nlTestSuite * inSuite, void * inContext)
69 {
70     SetupPayload payload = GetDefaultPayload();
71
72     QRCodeSetupPayloadGenerator generator(payload);
73     string result;
74     CHIP_ERROR err  = generator.payloadBase41Representation(result);
75     bool didSucceed = err == CHIP_NO_ERROR;
76     NL_TEST_ASSERT(inSuite, didSucceed == true);
77
78     string expected = "CH:J20800G008008000";
79     NL_TEST_ASSERT(inSuite, result == expected);
80 }
81
82 void TestBase41(nlTestSuite * inSuite, void * inContext)
83 {
84     uint8_t input[] = { 10, 10, 10 };
85
86     // basic stuff
87     NL_TEST_ASSERT(inSuite, base41Encode(input, 0).empty());
88     NL_TEST_ASSERT(inSuite, base41Encode(input, 1) == "A");
89     NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "SL1");
90     NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL1A");
91
92     // test single odd byte corner conditions
93     input[2] = 0;
94     NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL10");
95     input[2] = 40;
96     NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL1.");
97     input[2] = 41;
98     NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL101");
99     input[2] = 255;
100     NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL196");
101
102     // testing optimized encoding
103     // verify that we can't optimize a low value, need 3 chars
104     input[0] = 255;
105     input[1] = 0;
106     NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "960");
107     // smallest optimized encoding, 256
108     input[0] = 256 % 256;
109     input[1] = 256 / 256;
110     NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "A6");
111     // largest optimizated encoding value
112     input[0] = ((kRadix * kRadix) - 1) % 256;
113     input[1] = ((kRadix * kRadix) - 1) / 256;
114     NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "..");
115     // can't optimize
116     input[0] = ((kRadix * kRadix)) % 256;
117     input[1] = ((kRadix * kRadix)) / 256;
118     NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "001");
119
120     // fun with strings
121     NL_TEST_ASSERT(inSuite, base41Encode((uint8_t *) "Hello World!", sizeof("Hello World!") - 1) == "GHF.KGL+48-G5LGK35");
122
123     vector<uint8_t> decoded = vector<uint8_t>();
124     NL_TEST_ASSERT(inSuite, base41Decode("GHF.KGL+48-G5LGK35", decoded) == CHIP_NO_ERROR);
125
126     string hello_world;
127     for (uint8_t b : decoded)
128     {
129         hello_world += static_cast<char>(b);
130     }
131     NL_TEST_ASSERT(inSuite, hello_world == "Hello World!");
132
133     // short input
134     NL_TEST_ASSERT(inSuite, base41Decode("A0", decoded) == CHIP_NO_ERROR);
135     NL_TEST_ASSERT(inSuite, decoded.size() == 1);
136
137     // empty == empty
138     NL_TEST_ASSERT(inSuite, base41Decode("", decoded) == CHIP_NO_ERROR);
139     NL_TEST_ASSERT(inSuite, decoded.empty());
140
141     // single base41 means one byte of output
142     NL_TEST_ASSERT(inSuite, base41Decode("A", decoded) == CHIP_NO_ERROR);
143     NL_TEST_ASSERT(inSuite, decoded.size() == 1);
144     NL_TEST_ASSERT(inSuite, decoded[0] == 10);
145
146     // outside valid chars
147     NL_TEST_ASSERT(inSuite, base41Decode("0\001", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
148     NL_TEST_ASSERT(inSuite, base41Decode("\0010", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
149     NL_TEST_ASSERT(inSuite, base41Decode("[0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
150     NL_TEST_ASSERT(inSuite, base41Decode("0[", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
151
152     // BOGUS chars
153     NL_TEST_ASSERT(inSuite, base41Decode("!0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
154     NL_TEST_ASSERT(inSuite, base41Decode("\"0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
155     NL_TEST_ASSERT(inSuite, base41Decode("#0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
156     NL_TEST_ASSERT(inSuite, base41Decode("&0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
157     NL_TEST_ASSERT(inSuite, base41Decode("'0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
158     NL_TEST_ASSERT(inSuite, base41Decode("(0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
159     NL_TEST_ASSERT(inSuite, base41Decode(")0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
160     NL_TEST_ASSERT(inSuite, base41Decode(",0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
161     NL_TEST_ASSERT(inSuite, base41Decode(";0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
162     NL_TEST_ASSERT(inSuite, base41Decode("<0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
163     NL_TEST_ASSERT(inSuite, base41Decode("=0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
164     NL_TEST_ASSERT(inSuite, base41Decode(">0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
165     NL_TEST_ASSERT(inSuite, base41Decode("@0", decoded) == CHIP_ERROR_INVALID_INTEGER_VALUE);
166
167     // odd byte(s) cases
168     NL_TEST_ASSERT(inSuite, base41Decode("96", decoded) == CHIP_NO_ERROR); // this is 255
169     NL_TEST_ASSERT(inSuite, decoded.size() == 1 && decoded[0] == 255);
170     NL_TEST_ASSERT(inSuite, base41Decode("A6", decoded) == CHIP_NO_ERROR); // this is 256, needs 2 output bytes
171     NL_TEST_ASSERT(inSuite, decoded.size() == 2 && decoded[0] + decoded[1] * 256 == 256);
172     NL_TEST_ASSERT(inSuite, base41Decode("..", decoded) == CHIP_NO_ERROR); // this is (41*41)-1, or 1680, needs 2 output bytes
173     NL_TEST_ASSERT(inSuite, decoded.size() == 2 && decoded[0] + decoded[1] * 256 == (kRadix * kRadix) - 1);
174 }
175
176 void TestBitsetLen(nlTestSuite * inSuite, void * inContext)
177 {
178     NL_TEST_ASSERT(inSuite, kTotalPayloadDataSizeInBits % 8 == 0);
179 }
180
181 void TestSetupPayloadVerify(nlTestSuite * inSuite, void * inContext)
182 {
183     SetupPayload payload = GetDefaultPayload();
184     NL_TEST_ASSERT(inSuite, payload.isValidQRCodePayload() == true);
185
186     // test invalid version
187     SetupPayload test_payload = payload;
188     test_payload.version      = 1 << kVersionFieldLengthInBits;
189     NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
190
191     // test invalid rendezvousInformation
192     test_payload                       = payload;
193     test_payload.rendezvousInformation = static_cast<RendezvousInformationFlags>(1 << kRendezvousInfoFieldLengthInBits);
194     NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
195
196     // test invalid rendezvousInformation
197     test_payload = payload;
198     test_payload.rendezvousInformation =
199         static_cast<RendezvousInformationFlags>(static_cast<uint16_t>(RendezvousInformationFlags::kAllMask) + 1);
200     NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
201
202     // test invalid discriminator
203     test_payload               = payload;
204     test_payload.discriminator = 1 << kPayloadDiscriminatorFieldLengthInBits;
205     NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
206
207     // test invalid stetup PIN
208     test_payload              = payload;
209     test_payload.setUpPINCode = 1 << kSetupPINCodeFieldLengthInBits;
210     NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
211 }
212
213 void TestInvalidQRCodePayload_WrongCharacterSet(nlTestSuite * inSuite, void * inContext)
214 {
215     string invalidString = "adas12AA";
216
217     QRCodeSetupPayloadParser parser = QRCodeSetupPayloadParser(invalidString);
218     SetupPayload payload;
219     CHIP_ERROR err = parser.populatePayload(payload);
220     bool didFail   = err != CHIP_NO_ERROR;
221     NL_TEST_ASSERT(inSuite, didFail == true);
222     NL_TEST_ASSERT(inSuite, payload.isValidQRCodePayload() == false);
223 }
224
225 void TestInvalidQRCodePayload_WrongLength(nlTestSuite * inSuite, void * inContext)
226 {
227     string invalidString            = "AA12";
228     QRCodeSetupPayloadParser parser = QRCodeSetupPayloadParser(invalidString);
229     SetupPayload payload;
230     CHIP_ERROR err = parser.populatePayload(payload);
231     bool didFail   = err != CHIP_NO_ERROR;
232     NL_TEST_ASSERT(inSuite, didFail == true);
233     NL_TEST_ASSERT(inSuite, payload.isValidQRCodePayload() == false);
234 }
235
236 void TestPayloadEquality(nlTestSuite * inSuite, void * inContext)
237 {
238     SetupPayload payload      = GetDefaultPayload();
239     SetupPayload equalPayload = GetDefaultPayload();
240
241     bool result = payload == equalPayload;
242     NL_TEST_ASSERT(inSuite, result == true);
243 }
244
245 void TestPayloadInEquality(nlTestSuite * inSuite, void * inContext)
246 {
247     SetupPayload payload = GetDefaultPayload();
248
249     SetupPayload unequalPayload  = GetDefaultPayload();
250     unequalPayload.discriminator = 28;
251     unequalPayload.setUpPINCode  = 121233;
252
253     bool result = payload == unequalPayload;
254     NL_TEST_ASSERT(inSuite, result == false);
255 }
256
257 void TestQRCodeToPayloadGeneration(nlTestSuite * inSuite, void * inContext)
258 {
259     SetupPayload payload = GetDefaultPayload();
260
261     QRCodeSetupPayloadGenerator generator(payload);
262     string base41Rep;
263     CHIP_ERROR err  = generator.payloadBase41Representation(base41Rep);
264     bool didSucceed = err == CHIP_NO_ERROR;
265     NL_TEST_ASSERT(inSuite, didSucceed == true);
266
267     SetupPayload resultingPayload;
268     QRCodeSetupPayloadParser parser(base41Rep);
269
270     err        = parser.populatePayload(resultingPayload);
271     didSucceed = err == CHIP_NO_ERROR;
272     NL_TEST_ASSERT(inSuite, didSucceed == true);
273     NL_TEST_ASSERT(inSuite, resultingPayload.isValidQRCodePayload() == true);
274
275     bool result = payload == resultingPayload;
276     NL_TEST_ASSERT(inSuite, result == true);
277 }
278
279 void TestExtractPayload(nlTestSuite * inSuite, void * inContext)
280 {
281     NL_TEST_ASSERT(inSuite, extractPayload(string("CH:ABC")) == string("ABC"));
282     NL_TEST_ASSERT(inSuite, extractPayload(string("CH:")) == string(""));
283     NL_TEST_ASSERT(inSuite, extractPayload(string("H:")) == string(""));
284     NL_TEST_ASSERT(inSuite, extractPayload(string("ASCH:")) == string(""));
285     NL_TEST_ASSERT(inSuite, extractPayload(string("Z%CH:ABC%")) == string("ABC"));
286     NL_TEST_ASSERT(inSuite, extractPayload(string("Z%CH:ABC")) == string("ABC"));
287     NL_TEST_ASSERT(inSuite, extractPayload(string("%Z%CH:ABC")) == string("ABC"));
288     NL_TEST_ASSERT(inSuite, extractPayload(string("%Z%CH:ABC%")) == string("ABC"));
289     NL_TEST_ASSERT(inSuite, extractPayload(string("%Z%CH:ABC%DDD")) == string("ABC"));
290     NL_TEST_ASSERT(inSuite, extractPayload(string("CH:ABC%DDD")) == string("ABC"));
291     NL_TEST_ASSERT(inSuite, extractPayload(string("CH:ABC%")) == string("ABC"));
292     NL_TEST_ASSERT(inSuite, extractPayload(string("%CH:")) == string(""));
293     NL_TEST_ASSERT(inSuite, extractPayload(string("%CH:%")) == string(""));
294     NL_TEST_ASSERT(inSuite, extractPayload(string("A%")) == string(""));
295     NL_TEST_ASSERT(inSuite, extractPayload(string("CH:%")) == string(""));
296     NL_TEST_ASSERT(inSuite, extractPayload(string("%CH:ABC")) == string("ABC"));
297     NL_TEST_ASSERT(inSuite, extractPayload(string("ABC")) == string(""));
298 }
299
300 // Test Suite
301
302 /**
303  *  Test Suite that lists all the test functions.
304  */
305 // clang-format off
306 const nlTest sTests[] =
307 {
308     NL_TEST_DEF("Test Rendezvous Flags",                                            TestRendezvousFlags),
309     NL_TEST_DEF("Test Base 41",                                                     TestBase41),
310     NL_TEST_DEF("Test Bitset Length",                                               TestBitsetLen),
311     NL_TEST_DEF("Test Payload Byte Array Representation",                           TestPayloadByteArrayRep),
312     NL_TEST_DEF("Test Payload Base 41 Representation",                              TestPayloadBase41Rep),
313     NL_TEST_DEF("Test Setup Payload Verify",                                        TestSetupPayloadVerify),
314     NL_TEST_DEF("Test Payload Equality",                                            TestPayloadEquality),
315     NL_TEST_DEF("Test Payload Inequality",                                          TestPayloadInEquality),
316     NL_TEST_DEF("Test QRCode to Payload Generation",                                TestQRCodeToPayloadGeneration),
317     NL_TEST_DEF("Test Invalid QR Code Payload - Wrong Character Set",               TestInvalidQRCodePayload_WrongCharacterSet),
318     NL_TEST_DEF("Test Invalid QR Code Payload - Wrong  Length",                     TestInvalidQRCodePayload_WrongLength),
319     NL_TEST_DEF("Test Extract Payload",                                             TestExtractPayload),
320
321     NL_TEST_SENTINEL()
322 };
323 // clang-format on
324
325 struct TestContext
326 {
327     nlTestSuite * mSuite;
328 };
329
330 } // namespace
331
332 /**
333  *  Main
334  */
335 int TestQuickResponseCode()
336 {
337     // clang-format off
338     nlTestSuite theSuite =
339     {
340         "chip-qrcode-general-tests",
341         &sTests[0],
342         nullptr,
343         nullptr
344     };
345     // clang-format on
346     TestContext context;
347
348     context.mSuite = &theSuite;
349
350     // Generate machine-readable, comma-separated value (CSV) output.
351     nl_test_set_output_style(OUTPUT_CSV);
352
353     // Run test suit against one context
354     nlTestRunner(&theSuite, &context);
355
356     return nlTestRunnerStats(&theSuite);
357 }
358
359 CHIP_REGISTER_TEST_SUITE(TestQuickResponseCode);