blob: 3754e14699c2d1382ee068dd70e42fe3f65b38f2 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/public/common/privacy_budget/identifiable_token_builder.h"
#include <cstdint>
#include <vector>
#include "base/containers/span.h"
#include "base/rand_util.h"
#include "base/strings/strcat.h"
#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/privacy_budget/identifiable_token.h"
namespace blink {
TEST(IdentifiableTokenBuilderTest, Empty) {
IdentifiableTokenBuilder sample;
EXPECT_EQ(IdentifiableToken(INT64_C(0x5ad32e10d3a3c2b5)), sample.GetToken());
}
TEST(IdentifiableTokenBuilderTest, ConstructVsAdd) {
const char kOneByte[1] = {'a'};
IdentifiableTokenBuilder add_token;
add_token.AddBytes(base::as_bytes(base::make_span(kOneByte)));
IdentifiableTokenBuilder construct_token(
base::as_bytes(base::make_span(kOneByte)));
EXPECT_EQ(add_token.GetToken(), construct_token.GetToken());
}
TEST(IdentifiableTokenBuilderTest, OneByte) {
const char kOneByte[1] = {'a'};
IdentifiableTokenBuilder sample;
sample.AddBytes(base::as_bytes(base::make_span(kOneByte)));
EXPECT_EQ(IdentifiableToken(INT64_C(0x6de50a5cefa7ba0e)), sample.GetToken());
}
TEST(IdentifiableTokenBuilderTest, TwoBytesInTwoTakes) {
const char kBytes[] = {'a', 'b'};
auto bytes_span = base::as_bytes(base::span<const char>(kBytes));
IdentifiableTokenBuilder whole_span_token(bytes_span);
IdentifiableTokenBuilder two_parts_token;
two_parts_token.AddBytes(bytes_span.first(1));
two_parts_token.AddBytes(bytes_span.last(1));
EXPECT_EQ(whole_span_token.GetToken(), two_parts_token.GetToken());
}
TEST(IdentifiableTokenBuilderTest, SixtySixBytesInTwoTakes) {
constexpr size_t kSize = 66;
std::vector<char> big_array(kSize, 'a');
auto bytes_span = base::as_bytes(base::span<const char>(big_array));
IdentifiableTokenBuilder whole_span_token(bytes_span);
IdentifiableTokenBuilder two_parts_token;
two_parts_token.AddBytes(bytes_span.first(kSize / 2));
two_parts_token.AddBytes(bytes_span.last(kSize / 2));
EXPECT_EQ(whole_span_token.GetToken(), two_parts_token.GetToken());
}
TEST(IdentifiableTokenBuilderTest, AddValue) {
const auto kExpectedToken = IdentifiableToken(INT64_C(0xe475af2a732298e2));
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(INT8_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(UINT8_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(INT16_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(UINT16_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(INT32_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(UINT32_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(INT64_C(1)).GetToken());
EXPECT_EQ(kExpectedToken,
IdentifiableTokenBuilder().AddValue(UINT64_C(1)).GetToken());
}
TEST(IdentifiableTokenBuilderTest, AddAtomic_AlwaysConstant) {
const uint8_t kS1[] = {1, 2, 3, 4, 5};
EXPECT_EQ(
IdentifiableToken(INT64_C(0xfaeb0b8e769729b9)),
IdentifiableTokenBuilder().AddAtomic(base::make_span(kS1)).GetToken());
}
TEST(IdentifiableTokenBuilderTest, AddAtomic_PadSuffix) {
const uint8_t kS1[] = {1, 2, 3, 4, 5};
const uint8_t kS1_padded[] = {5, 0, 0, 0, 0, 0, 0, 0, // Little endian 5
1, 2, 3, 4, 5, 0, 0, 0};
EXPECT_EQ(
IdentifiableTokenBuilder().AddAtomic(base::make_span(kS1)).GetToken(),
IdentifiableTokenBuilder()
.AddBytes(base::make_span(kS1_padded))
.GetToken());
}
TEST(IdentifiableTokenBuilderTest, AddAtomic_PadPrefix) {
const uint8_t kS2_pre[] = {1, 2};
const uint8_t kS2[] = {3, 4, 5};
const uint8_t kS2_padded[] = {1, 2, 0, 0, 0, 0, 0, 0,
3, 0, 0, 0, 0, 0, 0, 0, // Little endian 3
3, 4, 5, 0, 0, 0, 0, 0};
EXPECT_EQ(IdentifiableTokenBuilder()
.AddBytes(base::make_span(kS2_pre))
.AddAtomic(base::make_span(kS2))
.GetToken(),
IdentifiableTokenBuilder()
.AddBytes(base::make_span(kS2_padded))
.GetToken());
}
TEST(IdentifiableTokenBuilderTest, AddVsAddAtomic) {
const uint8_t kA1[] = {'a', 'b', 'c', 'd'};
const uint8_t kA2[] = {'e', 'f', 'g', 'h'};
const uint8_t kB1[] = {'a'};
const uint8_t kB2[] = {'b', 'c', 'd', 'e', 'f', 'g', 'h'};
// Adding buffers wth AddBytes() doesn't distinguish between the two
// partitions, and this is intentional.
IdentifiableTokenBuilder builder_A;
builder_A.AddBytes(base::make_span(kA1));
builder_A.AddBytes(base::make_span(kA2));
auto token_for_A = builder_A.GetToken();
IdentifiableTokenBuilder builder_B;
builder_B.AddBytes(base::make_span(kB1));
builder_B.AddBytes(base::make_span(kB2));
auto token_for_B = builder_B.GetToken();
EXPECT_EQ(token_for_A, token_for_B);
// However AtomicAdd distinguishes between the two.
IdentifiableTokenBuilder atomic_A;
atomic_A.AddAtomic(base::make_span(kA1));
atomic_A.AddAtomic(base::make_span(kA2));
auto atomic_token_for_A = atomic_A.GetToken();
IdentifiableTokenBuilder atomic_B;
atomic_B.AddAtomic(base::make_span(kB1));
atomic_B.AddAtomic(base::make_span(kB2));
auto atomic_token_for_B = atomic_B.GetToken();
EXPECT_NE(atomic_token_for_A, atomic_token_for_B);
}
TEST(IdentifiableTokenBuilderTest, LotsOfRandomPartitions) {
constexpr size_t kLargeBufferSize = 1000 * 1000 * 10;
constexpr size_t kCycle = 149; // A prime
std::vector<uint8_t> data(kLargeBufferSize);
for (size_t i = 0; i < kLargeBufferSize; ++i) {
data[i] = i % kCycle;
}
int partition_count = base::RandInt(50, 500);
// Pick |partition_count| random numbers between 0 and kLargeBufferSize (half
// open) that will indicate where the partitions are. In reality there will be
// |partition_count + 1| partitions:
//
// |<--------->:<------------>:<--- .. ...->:<---------------------->|
// | : : .. ... : |
// 0 partitions[0] partitions[1] .. partitions[n-1] kLargeBufferSize
std::vector<int> partitions;
for (int i = 0; i < partition_count; ++i)
partitions.push_back(base::RandInt(0, kLargeBufferSize));
std::sort(partitions.begin(), partitions.end());
std::string trace;
base::StringAppendF(&trace, "Partitions[%d]={0", partition_count + 2);
for (auto p : partitions)
base::StringAppendF(&trace, ", %d", p);
base::StringAppendF(&trace, ", %zu}", kLargeBufferSize);
SCOPED_TRACE(trace);
IdentifiableTokenBuilder partitioned_sample;
int low = 0;
for (auto high : partitions) {
ASSERT_LE(low, high);
partitioned_sample.AddBytes(base::make_span(&data[low], high - low));
low = high;
}
partitioned_sample.AddBytes(
base::make_span(&data[low], kLargeBufferSize - low));
IdentifiableTokenBuilder full_buffer_sample(data);
EXPECT_EQ(full_buffer_sample.GetToken(), partitioned_sample.GetToken());
}
} // namespace blink