[go: nahoru, domu]

blob: 560a5a39925aeb065820aec47533b890f5f17319 [file] [log] [blame]
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/qr_code_generator/qr_code_generator.h"
#include <limits>
#include <optional>
#include "base/containers/span.h"
#include "base/test/scoped_feature_list.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace qr_code_generator {
TEST(QRCodeGeneratorTest, Generate) {
// Without a QR decoder implementation, there's a limit to how much we can
// test the QR encoder. Therefore this test just runs a generation to ensure
// that no DCHECKs are hit and that the output has the correct structure. When
// run under ASan, this will also check that every byte of the output has been
// written to.
constexpr size_t kMaxInputLen = 210;
uint8_t input[kMaxInputLen];
std::optional<int> smallest_size;
std::optional<int> largest_size;
for (const bool use_alphanum : {false, true}) {
SCOPED_TRACE(use_alphanum);
// 'A' is in the alphanumeric set, but 'a' is not.
memset(input, use_alphanum ? 'A' : 'a', sizeof(input));
for (size_t input_len = 30; input_len < kMaxInputLen; input_len += 10) {
SCOPED_TRACE(input_len);
base::expected<GeneratedCode, Error> qr_code =
GenerateCode(base::span<const uint8_t>(input, input_len));
ASSERT_TRUE(qr_code.has_value());
auto& qr_data = qr_code->data;
if (!smallest_size || qr_code->qr_size < *smallest_size) {
smallest_size = qr_code->qr_size;
}
if (!largest_size || qr_code->qr_size > *largest_size) {
largest_size = qr_code->qr_size;
}
int index = 0;
for (int y = 0; y < qr_code->qr_size; y++) {
for (int x = 0; x < qr_code->qr_size; x++) {
ASSERT_EQ(0, qr_data[index++] & 0b11111100);
}
}
}
}
// The generator should generate a variety of QR sizes.
ASSERT_TRUE(smallest_size);
ASSERT_TRUE(largest_size);
ASSERT_LT(*smallest_size, *largest_size);
}
TEST(QRCodeGeneratorTest, ManySizes) {
// Test multiple input sizes. This test was originally designed to test for
// memory safety problems caused by off-by-one bugs in the old C++
// implementation. We are now shipping a memory-safe Rust implementation so we
// are now testing only sizes up to 90 - this helps to avoid flaky test
// timeouts.
std::string input = "";
std::map<int, size_t> max_input_length_for_qr_size;
for (;;) {
input.push_back('!');
if (input.size() > 90) {
break;
}
base::expected<GeneratedCode, Error> code =
GenerateCode(base::as_byte_span(input));
ASSERT_TRUE(code.has_value());
max_input_length_for_qr_size[code->qr_size] = input.size();
}
// Capacities taken from https://www.qrcode.com/en/about/version.html
//
// Rust supports all QR versions from 1 to 40 and defaults to M error
// correction.
//
// Other versions skipped, because otherwise the test may timeout.
EXPECT_EQ(max_input_length_for_qr_size[21], 14u); // 1-M
EXPECT_EQ(max_input_length_for_qr_size[25], 26u); // 2-M
EXPECT_EQ(max_input_length_for_qr_size[29], 42u); // 3-M
EXPECT_EQ(max_input_length_for_qr_size[33], 62u); // 4-M
EXPECT_EQ(max_input_length_for_qr_size[37], 84u); // 5-M
}
// Test helper that returns `GeneratedCode::qr_size` or -1 if there was a
// failure.
int GenerateAndGetQrCodeSize(size_t input_size) {
std::string input(input_size, '!');
base::expected<GeneratedCode, Error> code =
GenerateCode(base::as_byte_span(input));
return code.has_value() ? code->qr_size : -1;
}
TEST(QRCodeGeneratorTest, InputSize106) {
EXPECT_EQ(41, GenerateAndGetQrCodeSize(106u)); // 6-M
}
TEST(QRCodeGeneratorTest, InputSize122) {
EXPECT_EQ(45, GenerateAndGetQrCodeSize(122u)); // 7-M
}
TEST(QRCodeGeneratorTest, InputSize180) {
EXPECT_EQ(53, GenerateAndGetQrCodeSize(180u)); // 9-M
}
TEST(QRCodeGeneratorTest, InputSize287) {
EXPECT_EQ(65, GenerateAndGetQrCodeSize(287u)); // 12-M
}
TEST(QRCodeGeneratorTest, InputSize666) {
EXPECT_EQ(97, GenerateAndGetQrCodeSize(666u)); // 20-M
}
TEST(QRCodeGeneratorTest, HugeInput) {
// The numbers below have been taken from
// https://www.qrcode.com/en/about/version.html, for version = 40,
// ECC level = M.
const size_t kMaxInputSizeForNumericInputVersion40 = 5596;
const size_t kMaxInputSizeForBinaryInputVersion40 = 2331;
std::vector<uint8_t> huge_numeric_input(kMaxInputSizeForNumericInputVersion40,
'0');
std::vector<uint8_t> huge_binary_input(kMaxInputSizeForBinaryInputVersion40,
'\0');
// The Rust implementation can generate QR codes up to version 40.
ASSERT_TRUE(GenerateCode(huge_numeric_input).has_value());
ASSERT_TRUE(GenerateCode(huge_binary_input).has_value());
// Adding another character means that the inputs will no longer fit into QR
// code version 40 (as of year 2023 there are no further versions defined by
// the spec).
{
huge_numeric_input.push_back('0');
auto failure = GenerateCode(huge_numeric_input);
ASSERT_FALSE(failure.has_value());
EXPECT_EQ(failure.error(), Error::kInputTooLong);
}
{
huge_binary_input.push_back('\0');
auto failure = GenerateCode(huge_binary_input);
ASSERT_FALSE(failure.has_value());
EXPECT_EQ(failure.error(), Error::kInputTooLong);
}
}
TEST(QRCodeGeneratorTest, InvalidMinVersion) {
std::vector<uint8_t> input(123); // Arbitrary valid input.
{
auto failure = GenerateCode(input, std::make_optional(41));
ASSERT_FALSE(failure.has_value());
EXPECT_EQ(failure.error(), Error::kUnknownError);
}
{
auto failure = GenerateCode(
input, std::make_optional(std::numeric_limits<int>::max()));
ASSERT_FALSE(failure.has_value());
EXPECT_EQ(failure.error(), Error::kUnknownError);
}
{
auto failure = GenerateCode(input, std::make_optional(-1));
ASSERT_FALSE(failure.has_value());
EXPECT_EQ(failure.error(), Error::kUnknownError);
}
}
} // namespace qr_code_generator