3 * Copyright (c) 2020 Project CHIP Authors
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
20 * This file implements a unit test suite for the Quick Response
25 #include "TestHelpers.h"
27 #include <nlbyteorder.h>
28 #include <nlunit-test.h>
30 #include <support/UnitTestRegistration.h>
37 void TestRendezvousFlags(nlTestSuite * inSuite, void * inContext)
39 SetupPayload inPayload = GetDefaultPayload();
41 inPayload.rendezvousInformation = RendezvousInformationFlags::kNone;
42 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
44 inPayload.rendezvousInformation = RendezvousInformationFlags::kWiFi;
45 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
47 inPayload.rendezvousInformation = RendezvousInformationFlags::kBLE;
48 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
50 inPayload.rendezvousInformation = RendezvousInformationFlags::kThread;
51 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
53 inPayload.rendezvousInformation = RendezvousInformationFlags::kEthernet;
54 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
56 inPayload.rendezvousInformation = RendezvousInformationFlags::kAllMask;
57 NL_TEST_ASSERT(inSuite, CheckWriteRead(inPayload));
60 void TestPayloadByteArrayRep(nlTestSuite * inSuite, void * inContext)
62 SetupPayload payload = GetDefaultPayload();
64 string expected = " 00000 000000000000000100000000000 000010000000 00000001 0 0000000000000001 0000000000001100 101";
65 NL_TEST_ASSERT(inSuite, CompareBinary(payload, expected));
68 void TestPayloadBase41Rep(nlTestSuite * inSuite, void * inContext)
70 SetupPayload payload = GetDefaultPayload();
72 QRCodeSetupPayloadGenerator generator(payload);
74 CHIP_ERROR err = generator.payloadBase41Representation(result);
75 bool didSucceed = err == CHIP_NO_ERROR;
76 NL_TEST_ASSERT(inSuite, didSucceed == true);
78 string expected = "CH:J20800G008008000";
79 NL_TEST_ASSERT(inSuite, result == expected);
82 void TestBase41(nlTestSuite * inSuite, void * inContext)
84 uint8_t input[] = { 10, 10, 10 };
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");
92 // test single odd byte corner conditions
94 NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL10");
96 NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL1.");
98 NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL101");
100 NL_TEST_ASSERT(inSuite, base41Encode(input, 3) == "SL196");
102 // testing optimized encoding
103 // verify that we can't optimize a low value, need 3 chars
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) == "..");
116 input[0] = ((kRadix * kRadix)) % 256;
117 input[1] = ((kRadix * kRadix)) / 256;
118 NL_TEST_ASSERT(inSuite, base41Encode(input, 2) == "001");
121 NL_TEST_ASSERT(inSuite, base41Encode((uint8_t *) "Hello World!", sizeof("Hello World!") - 1) == "GHF.KGL+48-G5LGK35");
123 vector<uint8_t> decoded = vector<uint8_t>();
124 NL_TEST_ASSERT(inSuite, base41Decode("GHF.KGL+48-G5LGK35", decoded) == CHIP_NO_ERROR);
127 for (uint8_t b : decoded)
129 hello_world += static_cast<char>(b);
131 NL_TEST_ASSERT(inSuite, hello_world == "Hello World!");
134 NL_TEST_ASSERT(inSuite, base41Decode("A0", decoded) == CHIP_NO_ERROR);
135 NL_TEST_ASSERT(inSuite, decoded.size() == 1);
138 NL_TEST_ASSERT(inSuite, base41Decode("", decoded) == CHIP_NO_ERROR);
139 NL_TEST_ASSERT(inSuite, decoded.empty());
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);
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);
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);
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);
176 void TestBitsetLen(nlTestSuite * inSuite, void * inContext)
178 NL_TEST_ASSERT(inSuite, kTotalPayloadDataSizeInBits % 8 == 0);
181 void TestSetupPayloadVerify(nlTestSuite * inSuite, void * inContext)
183 SetupPayload payload = GetDefaultPayload();
184 NL_TEST_ASSERT(inSuite, payload.isValidQRCodePayload() == true);
186 // test invalid version
187 SetupPayload test_payload = payload;
188 test_payload.version = 1 << kVersionFieldLengthInBits;
189 NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
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);
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);
202 // test invalid discriminator
203 test_payload = payload;
204 test_payload.discriminator = 1 << kPayloadDiscriminatorFieldLengthInBits;
205 NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
207 // test invalid stetup PIN
208 test_payload = payload;
209 test_payload.setUpPINCode = 1 << kSetupPINCodeFieldLengthInBits;
210 NL_TEST_ASSERT(inSuite, test_payload.isValidQRCodePayload() == false);
213 void TestInvalidQRCodePayload_WrongCharacterSet(nlTestSuite * inSuite, void * inContext)
215 string invalidString = "adas12AA";
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);
225 void TestInvalidQRCodePayload_WrongLength(nlTestSuite * inSuite, void * inContext)
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);
236 void TestPayloadEquality(nlTestSuite * inSuite, void * inContext)
238 SetupPayload payload = GetDefaultPayload();
239 SetupPayload equalPayload = GetDefaultPayload();
241 bool result = payload == equalPayload;
242 NL_TEST_ASSERT(inSuite, result == true);
245 void TestPayloadInEquality(nlTestSuite * inSuite, void * inContext)
247 SetupPayload payload = GetDefaultPayload();
249 SetupPayload unequalPayload = GetDefaultPayload();
250 unequalPayload.discriminator = 28;
251 unequalPayload.setUpPINCode = 121233;
253 bool result = payload == unequalPayload;
254 NL_TEST_ASSERT(inSuite, result == false);
257 void TestQRCodeToPayloadGeneration(nlTestSuite * inSuite, void * inContext)
259 SetupPayload payload = GetDefaultPayload();
261 QRCodeSetupPayloadGenerator generator(payload);
263 CHIP_ERROR err = generator.payloadBase41Representation(base41Rep);
264 bool didSucceed = err == CHIP_NO_ERROR;
265 NL_TEST_ASSERT(inSuite, didSucceed == true);
267 SetupPayload resultingPayload;
268 QRCodeSetupPayloadParser parser(base41Rep);
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);
275 bool result = payload == resultingPayload;
276 NL_TEST_ASSERT(inSuite, result == true);
279 void TestExtractPayload(nlTestSuite * inSuite, void * inContext)
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(""));
303 * Test Suite that lists all the test functions.
306 const nlTest sTests[] =
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),
327 nlTestSuite * mSuite;
335 int TestQuickResponseCode()
338 nlTestSuite theSuite =
340 "chip-qrcode-general-tests",
348 context.mSuite = &theSuite;
350 // Generate machine-readable, comma-separated value (CSV) output.
351 nl_test_set_output_style(OUTPUT_CSV);
353 // Run test suit against one context
354 nlTestRunner(&theSuite, &context);
356 return nlTestRunnerStats(&theSuite);
359 CHIP_REGISTER_TEST_SUITE(TestQuickResponseCode);