blob: bb8cee68003833b1c018db38bc17d474950cf9db [file] [log] [blame]
// Copyright 2016 The Fuchsia 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 "peridot/lib/firebase/encoding.h"
#include <string>
#include <src/lib/fxl/strings/utf_codecs.h>
#include <zircon/syscalls.h>
#include "gtest/gtest.h"
namespace firebase {
namespace {
// Allows to create correct std::strings with \0 bytes inside from C-style
// string constants.
std::string operator"" _s(const char* str, size_t size) {
return std::string(str, size);
}
// See
// https://www.firebase.com/docs/rest/guide/understanding-data.html#section-limitations
bool IsValidKey(const std::string& s) {
if (!fxl::IsStringUTF8(s)) {
return false;
}
for (const char& c : s) {
if (static_cast<unsigned char>(c) <= 31) {
return false;
}
if (c == 127) {
return false;
}
if (c == '+' || c == '$' || c == '[' || c == ']' || c == '/' || c == '\"' ||
c == '\\') {
return false;
}
}
return true;
}
bool IsValidValue(const std::string s) {
if (!fxl::IsStringUTF8(s)) {
return false;
}
for (const char& c : s) {
if (static_cast<unsigned char>(c) <= 31) {
return false;
}
if (c == 127) {
return false;
}
if (c == '\"' || c == '\\') {
return false;
}
}
return true;
}
TEST(EncodingTest, BackAndForth) {
std::string s;
std::string ret_key;
std::string ret_value;
s = "";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "abcdef";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "leśna łączka";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "\x02\x7F";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "\xFF";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "\x01";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "abc\"def\"ghi'jkl'";
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "\0\0\0"_s;
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "bazinga\0\0\0"_s;
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
s = "alice\0bob"_s;
EXPECT_TRUE(Decode(EncodeKey(s), &ret_key));
EXPECT_EQ(s, ret_key);
EXPECT_TRUE(Decode(EncodeValue(s), &ret_value));
EXPECT_EQ(s, ret_value);
// Check random sequence of size less or equals 3.
for (size_t i = 0; i < 10000; ++i) {
size_t size = 0;
zx_cprng_draw(&size, sizeof(size));
size = size % 4;
char buffer[size];
fxl::StringView view(buffer, size);
zx_cprng_draw(reinterpret_cast<unsigned char*>(buffer), size);
EXPECT_TRUE(Decode(EncodeKey(view), &ret_key));
EXPECT_EQ(view, ret_key);
EXPECT_TRUE(Decode(EncodeValue(view), &ret_value));
EXPECT_EQ(view, ret_value);
}
}
TEST(EncodingTest, Keys) {
EXPECT_EQ("V", EncodeKey(""));
EXPECT_EQ("abcV", EncodeKey("abc"));
EXPECT_EQ("qwerty123V", EncodeKey("qwerty123"));
EXPECT_EQ("YWJjLw==B", EncodeKey("abc/"));
EXPECT_EQ("I1tdIQ==B", EncodeKey("#[]!"));
EXPECT_EQ("fw==B", EncodeKey("\x7F"));
EXPECT_EQ("_w==B", EncodeKey("\xFF"));
EXPECT_EQ("Ig==B", EncodeKey("\""));
EXPECT_EQ("Kw==B", EncodeKey("+"));
}
TEST(EncodingTest, Values) {
EXPECT_EQ("V", EncodeValue(""));
EXPECT_EQ("abcV", EncodeValue("abc"));
EXPECT_EQ("qwerty123V", EncodeValue("qwerty123"));
EXPECT_EQ("abc/V", EncodeValue("abc/"));
EXPECT_EQ("#[]!V", EncodeValue("#[]!"));
EXPECT_EQ("fw==B", EncodeValue("\x7F"));
EXPECT_EQ("_w==B", EncodeValue("\xFF"));
EXPECT_EQ("Ig==B", EncodeValue("\""));
EXPECT_EQ("Iy9cIT9bXQ==B", EncodeValue("#/\\!?[]"));
EXPECT_EQ("+V", EncodeValue("+"));
}
TEST(EncodingTest, ValidKeys) {
std::string original;
std::string encoded;
std::string decoded;
original = "\x02, \x7F, \x18, \x1D are forbidden, [], $ and / too!";
encoded = EncodeKey(original);
EXPECT_TRUE(IsValidKey(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "\xFF";
encoded = EncodeKey(original);
EXPECT_TRUE(IsValidKey(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "\xFF\x7F\x05\x09\xFF\xFF\x0B";
encoded = EncodeKey(original);
EXPECT_TRUE(IsValidKey(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "zażółć gęślą jaźń\xFF\xFF";
encoded = EncodeKey(original);
EXPECT_TRUE(IsValidKey(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
}
TEST(EncodingTest, ValidValues) {
std::string original;
std::string encoded;
std::string decoded;
original = "\x02, \x7F, \x18, \x1D are ok, [], $ and / too!";
encoded = EncodeValue(original);
EXPECT_TRUE(IsValidValue(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "\x01\x00\x02"_s;
encoded = EncodeValue(original);
EXPECT_TRUE(IsValidValue(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "\xFF";
encoded = EncodeValue(original);
EXPECT_TRUE(IsValidValue(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "\xFF\x7F\x05\x09\xFF\xFF\x0B";
encoded = EncodeValue(original);
EXPECT_TRUE(IsValidValue(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
original = "zażółć gęślą jaźń\xFF\xFF";
encoded = EncodeValue(original);
EXPECT_TRUE(IsValidValue(encoded));
EXPECT_TRUE(Decode(encoded, &decoded));
EXPECT_EQ(original, decoded);
}
} // namespace
} // namespace firebase