// Copyright 2017 The Abseil Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "absl/strings/escaping.h"
#include <array>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <initializer_list>
#include <memory>
#include <string>
#include <vector>
#include "gtest/gtest.h"
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "absl/strings/internal/escaping_test_common.h"
#include "absl/strings/string_view.h"
namespace {
struct epair {
std::string escaped;
std::string unescaped;
};
TEST(CEscape, EscapeAndUnescape) {
const std::string inputs[] = {
std::string("foo\nxx\r\b\0023"),
std::string(""),
std::string("abc"),
std::string("\1chad_rules"),
std::string("\1arnar_drools"),
std::string("xxxx\r\t'\"\\"),
std::string("\0xx\0", 4),
std::string("\x01\x31"),
std::string("abc\xb\x42\141bc"),
std::string("123\1\x31\x32\x33"),
std::string("\xc1\xca\x1b\x62\x19o\xcc\x04"),
std::string(
"\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name"),
};
// Do this twice, once for octal escapes and once for hex escapes.
for (int kind = 0; kind < 4; kind++) {
for (const std::string& original : inputs) {
std::string escaped;
switch (kind) {
case 0:
escaped = absl::CEscape(original);
break;
case 1:
escaped = absl::CHexEscape(original);
break;
case 2:
escaped = absl::Utf8SafeCEscape(original);
break;
case 3:
escaped = absl::Utf8SafeCHexEscape(original);
break;
}
std::string unescaped_str;
EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str));
EXPECT_EQ(unescaped_str, original);
unescaped_str.erase();
std::string error;
EXPECT_TRUE(absl::CUnescape(escaped, &unescaped_str, &error));
EXPECT_EQ(error, "");
// Check in-place unescaping
std::string s = escaped;
EXPECT_TRUE(absl::CUnescape(s, &s));
ASSERT_EQ(s, original);
}
}
// Check that all possible two character strings can be escaped then
// unescaped successfully.
for (int char0 = 0; char0 < 256; char0++) {
for (int char1 = 0; char1 < 256; char1++) {
char chars[2];
chars[0] = char0;
chars[1] = char1;
std::string s(chars, 2);
std::string escaped = absl::CHexEscape(s);
std::string unescaped;
EXPECT_TRUE(absl::CUnescape(escaped, &unescaped));
EXPECT_EQ(s, unescaped);
}
}
}
TEST(CEscape, BasicEscaping) {
epair oct_values[] = {
{"foo\\rbar\\nbaz\\t", "foo\rbar\nbaz\t"},
{"\\'full of \\\"sound\\\" and \\\"fury\\\"\\'",
"'full of \"sound\" and \"fury\"'"},
{"signi\\\\fying\\\\ nothing\\\\", "signi\\fying\\ nothing\\"},
{"\\010\\t\\n\\013\\014\\r", "\010\011\012\013\014\015"}
};
epair hex_values[] = {
{"ubik\\rubik\\nubik\\t", "ubik\rubik\nubik\t"},
{"I\\\'ve just seen a \\\"face\\\"",
"I've just seen a \"face\""},
{"hel\\\\ter\\\\skel\\\\ter\\\\", "hel\\ter\\skel\\ter\\"},
{"\\x08\\t\\n\\x0b\\x0c\\r", "\010\011\012\013\014\015"}
};
epair utf8_oct_values[] = {
{"\xe8\xb0\xb7\xe6\xad\x8c\\r\xe8\xb0\xb7\xe6\xad\x8c\\nbaz\\t",
"\xe8\xb0\xb7\xe6\xad\x8c\r\xe8\xb0\xb7\xe6\xad\x8c\nbaz\t"},
{"\\\"\xe8\xb0\xb7\xe6\xad\x8c\\\" is Google\\\'s Chinese name",
"\"\xe8\xb0\xb7\xe6\xad\x8c\" is Google\'s Chinese name"},
{"\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\xab\\\\are\\\\Japanese\\\\chars\\\\",
"\xe3\x83\xa1\xe3\x83\xbc\xe3\x83\xab\\are\\Japanese\\chars\\"},
{"\xed\x81\xac\xeb\xa1\xac\\010\\t\\n\\013\\014\\r",
"\xed\x81\xac\xeb\xa1\xac\010\011\012\013\014\015"}
};
epair utf8_hex_values[] = {
{"\x20\xe4\xbd\xa0\\t\xe5\xa5\xbd,\\r!\\n",
"\x20\xe4\xbd\xa0\t\xe5\xa5\xbd,\r!\n"},
{"\xe8\xa9\xa6\xe9\xa8\x93\\\' means \\\"test\\\"",
"\xe8\xa9\xa6\xe9\xa8\x93\' means \"test\""},
{"\\\\\xe6\x88\x91\\\\:\\\\\xe6\x9d\xa8\xe6\xac\xa2\\\\",
"\\\xe6\x88\x91\\:\\\xe6\x9d\xa8\xe6\xac\xa2\\"},
{"\xed\x81\xac\xeb\xa1\xac\\x08\\t\\n\\x0b\\x0c\\r",
"\xed\x81\xac\xeb\xa1\xac\010\011\012\013\014\015"}
};
for (const epair& val : oct_values) {
std::string escaped = absl::CEscape(val.unescaped);
EXPECT_EQ(escaped, val.escaped);
}
for (const epair& val : hex_values) {
std::string escaped = absl::CHexEscape(val.unescaped);
EXPECT_EQ(escaped, val.escaped);
}
for (const epair& val : utf8_oct_values) {
std::string escaped = absl::Utf8SafeCEscape(val.unescaped);
EXPECT_EQ(escaped, val.escaped);
}
for (const epair& val : utf8_hex_values) {
std::string escaped = absl::Utf8SafeCHexEscape(val.unescaped);
EXPECT_EQ(escaped, val.escaped);
}
}
TEST(Unescape, BasicFunction) {
epair tests[] =
{{"", ""},
{"\\u0030", "0"},
{"\\u00A3", "\xC2\xA3"},
{"\\u22FD", "\xE2\x8B\xBD"},
{"\\U00010000", "\xF0\x90\x80\x80"},
{"\\U0010FFFD", "\xF4\x8F\xBF\xBD"}};
for (const epair& val : tests) {
std::string out;
EXPECT_TRUE(absl::CUnescape(val.escaped, &out));
EXPECT_EQ(out, val.unescaped);
}
std::string bad[] = {"\\u1", // too short
"\\U1", // too short
"\\Uffffff", // exceeds 0x10ffff (largest Unicode)
"\\U00110000", // exceeds 0x10ffff (largest Unicode)
"\\uD835", // surrogate character (D800-DFFF)
"\\U0000DD04", // surrogate character (D800-DFFF)
"\\777", // exceeds 0xff
"\\xABCD"}; // exceeds 0xff
for (const std::string& e : bad) {
std::string error;
std::string out;
EXPECT_FALSE(absl::CUnescape(e, &out, &error));
EXPECT_FALSE(error.empty());
out.erase();
EXPECT_FALSE(absl::CUnescape(e, &out));
}
}
class CUnescapeTest : public testing::Test {
protected:
static const char kStringWithMultipleOctalNulls[];
static const char kStringWithMultipleHexNulls[];
static const char kStringWithMultipleUnicodeNulls[];
std::string result_string_;
};
const char CUnescapeTest::kStringWithMultipleOctalNulls[] =
"\\0\\n" // null escape \0 plus newline
"0\\n" // just a number 0 (not a null escape) plus newline
"\\00\\12" // null escape \00 plus octal newline code
"\\000"; // null escape \000
// This has the same ingredients as kStringWithMultipleOctalNulls
// but with \x hex escapes instead of octal escapes.
const char CUnescapeTest::kStringWithMultipleHexNulls[] =
"\\x0\\n"
"0\\n"
"\\x00\\xa"
"\\x000";
const char CUnescapeTest::kStringWithMultipleUnicodeNulls[] =
"\\u0000\\n" // short-form (4-digit) null escape plus newline
"0\\n" // just a number 0 (not a null escape) plus newline
"\\U00000000"; // long-form (8-digit) null escape
TEST_F(CUnescapeTest, Unescapes1CharOctalNull) {
std::string original_string = "\\0";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes2CharOctalNull) {
std::string original_string = "\\00";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes3CharOctalNull) {
std::string original_string = "\\000";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes1CharHexNull) {
std::string original_string = "\\x0";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes2CharHexNull) {
std::string original_string = "\\x00";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes3CharHexNull) {
std::string original_string = "\\x000";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes4CharUnicodeNull) {
std::string original_string = "\\u0000";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, Unescapes8CharUnicodeNull) {
std::string original_string = "\\U00000000";
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0", 1), result_string_);
}
TEST_F(CUnescapeTest, UnescapesMultipleOctalNulls) {
std::string original_string(kStringWithMultipleOctalNulls);
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
// All escapes, including newlines and null escapes, should have been
// converted to the equivalent characters.
EXPECT_EQ(std::string("\0\n"
"0\n"
"\0\n"
"\0",
7),
result_string_);
}
TEST_F(CUnescapeTest, UnescapesMultipleHexNulls) {
std::string original_string(kStringWithMultipleHexNulls);
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0\n"
"0\n"
"\0\n"
"\0",
7),
result_string_);
}
TEST_F(CUnescapeTest, UnescapesMultipleUnicodeNulls) {
std::string original_string(kStringWithMultipleUnicodeNulls);
EXPECT_TRUE(absl::CUnescape(original_string, &result_string_));
EXPECT_EQ(std::string("\0\n"
"0\n"
"\0",
5),
result_string_);
}
static struct {
absl::string_view plaintext;
absl::string_view cyphertext;
} const base64_tests[] = {
// Empty string.
{{"", 0}, {"", 0}},
{{nullptr, 0},
{"", 0}}, // if length is zero, plaintext ptr must be ignored!
// Basic bit patterns;
// values obtained with "echo -n '...' | uuencode -m test"
{{"\000", 1}, "AA=="},
{{"\001", 1}, "AQ=="},
{{"\002", 1}, "Ag=="},
{{"\004", 1}, "BA=="},
{{"\010", 1}, "CA=="},
{{"\020", 1}, "EA=="},
{{"\040", 1}, "IA=="},
{{"\100", 1}, "QA=="},
{{"\200", 1}, "gA=="},
{{"\377", 1}, "/w=="},
{{"\376", 1}, "/g=="},
{{"\375", 1}, "/Q=="},
{{"\373", 1}, "+w=="},
{{"\367", 1}, "9w=="},
{{"\357", 1}, "7w=="},
{{"\337", 1}, "3w=="},
{{"\277", 1}, "vw=="},
{{"\177", 1}, "fw=="},
{{"\000\000", 2}, "AAA="},
{{"\000\001", 2}, "AAE="},
{{"\000\002", 2}, "AAI="},
{{"\000\004", 2}, "AAQ="},
{{"\000\010", 2}, "AAg="},
{{"\000\020", 2}, "ABA="},
{{"\000\040", 2}, "ACA="},
{{"\000\100", 2}, "AEA="},
{{"\000\200", 2}, "AIA="},
{{"\001\000", 2}, "AQA="},
{{"\002\000", 2}, "AgA="},
{{"\004\000", 2}, "BAA="},
{{"\010\000", 2}, "CAA="},
{{"\020\000", 2}, "EAA="},
{{"\040\000", 2}, "IAA="},
{{"\100\000", 2}, "QAA="},
{{"\200\000", 2}, "gAA="},
{{"\377\377", 2}, "//8="},
{{"\377\376", 2}, "//4="},
{{"\377\375", 2}, "//0="},
{{"\377\373", 2}, "//s="},
{{"\377\367", 2}, "//c="},
{{"\377\357", 2}, "/+8="},
{{"\377\337", 2}, "/98="},
{{"\377\277", 2}, "/78="},
{{"\377\177", 2}, "/38="},
{{"\376\377", 2}, "/v8="},
{{"\375\377", 2}, "/f8="},
{{"\373\377", 2}, "+/8="},
{{"\367\377", 2}, "9/8="},
{{"\357\377", 2}, "7/8="},
{{"\337\377", 2}, "3/8="},
{{"\277\377", 2}, "v/8="},
{{"\177\377", 2}, "f/8="},
{{"\000\000\000", 3}, "AAAA"},
{{"\000\000\001", 3}, "AAAB"},
{{"\000\000\002", 3}, "AAAC"},
{{"\000\000\004", 3}, "AAAE"},
{{"\000\000\010", 3}, "AAAI"},
{{"\000\000\020", 3}, "AAAQ"},
{{"\000\000\040", 3}, "AAAg"},
{{"\000\000\100", 3}, "AABA"},
{{"\000\000\200", 3}, "AACA"},
{{"\000\001\000", 3}, "AAEA"},
{{"\000\002\000", 3}, "AAIA"},
{{"\000\004\000", 3}, "AAQA"},
{{"\000\010\000", 3}, "AAgA"},
{{"\000\020\000", 3}, "ABAA"},
{{"\000\040\000", 3}, "ACAA"},
{{"\000\100\000", 3}, "AEAA"},
{{"\000\200\000", 3}, "AIAA"},
{{"\001\000\000", 3}, "AQAA"},
{{"\002\000\000", 3}, "AgAA"},
{{"\004\000\000", 3}, "BAAA"},
{{"\010\000\000", 3}, "CAAA"},
{{"\020\000\000", 3}, "EAAA"},
{{"\040\000\000", 3}, "IAAA"},
{{"\100\000\000", 3}, "QAAA"},
{{"\200\000\000", 3}, "gAAA"},
{{"\377\377\377", 3}, "////"},
{{"\377\377\376", 3}, "///+"},
{{"\377\377\375", 3}, "///9"},
{{"\377\377\373", 3}, "///7"},
{{"\377\377\367", 3}, "///3"},
{{"\377\377\357", 3}, "///v"},
{{"\377\377\337", 3}, "///f"},
{{"\377\377\277", 3}, "//+/"},
{{"\377\377\177", 3}, "//9/"},
{{"\377\376\377", 3}, "//7/"},
{{"\377\375\377", 3}, "//3/"},
{{"\377\373\377", 3}, "//v/"},
{{"\377\367\377", 3}, "//f/"},
{{"\377\357\377", 3}, "/+//"},
{{"\377\337\377", 3}, "/9//"},
{{"\377\277\377", 3}, "/7//"},
{{"\377\177\377", 3}, "/3//"},
{{"\376\377\377", 3}, "/v//"},
{{"\375\377\377", 3}, "/f//"},
{{"\373\377\377", 3}, "+///"},
{{"\367\377\377", 3}, "9///"},
{{"\357\377\377", 3}, "7///"},
{{"\337\377\377", 3}, "3///"},
{{"\277\377\377", 3}, "v///"},
{{"\177\377\377", 3}, "f///"},
// Random numbers: values obtained with
//
// #! /bin/bash
// dd bs=$1 count=1 if=/dev/random of=/tmp/bar.random
// od -N $1 -t o1 /tmp/bar.random
// uuencode -m test < /tmp/bar.random
//
// where $1 is the number of bytes (2, 3)
{{"\243\361", 2}, "o/E="},
{{"\024\167", 2}, "FHc="},
{{"\313\252", 2}, "y6o="},
{{"\046\041", 2}, "JiE="},
{{"\145\236", 2}, "ZZ4="},
{{"\254\325", 2}, "rNU="},
{{"\061\330", 2}, "Mdg="},
{{"\245\032", 2}, "pRo="},
{{"\006\000", 2}, "BgA="},
{{"\375\131", 2}, "/Vk="},
{{"\303\210", 2}, "w4g="},
{{"\040\037", 2}, "IB8="},
{{"\261\372", 2}, "sfo="},
{{"\335\014", 2}, "3Qw="},
{{"\233\217", 2}, "m48="},
{{"\373\056", 2}, "+y4="},
{{"\247\232", 2}, "p5o="},
{{"\107\053", 2}, "Rys="},
{{"\204\077", 2}, "hD8="},
{{"\276\211", 2}, "vok="},
{{"\313\110", 2}, "y0g="},
{{"\363\376", 2}, "8/4="},
{{"\251\234", 2}, "qZw="},
{{"\103\262", 2}, "Q7I="},
{{"\142\312", 2}, "Yso="},
{{"\067\211", 2}, "N4k="},
{{"\220\001", 2}, "kAE="},
{{"\152\240", 2}, "aqA="},
{{"\367\061", 2}, "9zE="},
{{"\133\255", 2}, "W60="},
{{"\176\035", 2}, "fh0="},
{{"\032\231", 2}, "Gpk="},
{{"\013\007\144", 3}, "Cwdk"},
{{"\030\112\106", 3}, "GEpG"},
{{"\047\325\046", 3}, "J9Um"},
{{"\310\160\022", 3}, "yHAS"},
{{"\131\100\237", 3}, "WUCf"},
{{"\064\342\134", 3}, "NOJc"},
{{"\010\177\004", 3}, "CH8E"},
{{"\345\147\205", 3}, "5WeF"},
{{"\300\343\360", 3}, "wOPw"},
{{"\061\240\201", 3}, "MaCB"},
{{"\225\333\044", 3}, "ldsk"},
{{"\215\137\352", 3}, "jV/q"},
{{"\371\147\160", 3}, "+Wdw"},
{{"\030\320\051", 3}, "GNAp"},
{{"\044\174\241", 3}, "JHyh"},
{{"\260\127\037", 3}, "sFcf"},
{{"\111\045\033", 3}, "SSUb"},
{{"\202\114\107", 3}, "gkxH"},
{{"\057\371\042", 3}, "L/ki"},
{{"\223\247\244", 3}, "k6ek"},
{{"\047\216\144", 3}, "J45k"},
{{"\203\070\327", 3}, "gzjX"},
{{"\247\140\072", 3}, "p2A6"},
{{"\124\115\116", 3}, "VE1O"},
{{"\157\162\050", 3}, "b3Io"},
{{"\357\223\004", 3}, "75ME"},
{{"\052\117\156", 3}, "Kk9u"},
{{"\347\154\000", 3}, "52wA"},
{{"\303\012\142", 3}, "wwpi"},
{{"\060\035\362", 3}, "MB3y"},
{{"\130\226\361", 3}, "WJbx"},
{{"\173\013\071", 3}, "ews5"},
{{"\336\004\027", 3}, "3gQX"},
{{"\357\366\234", 3}, "7/ac"},
{{"\353\304\111", 3}, "68RJ"},
{{"\024\264\131", 3}, "FLRZ"},
{{"\075\114\251", 3}, "PUyp"},
{{"\315\031\225", 3}, "zRmV"},
{{"\154\201\276", 3}, "bIG+"},
{{"\200\066\072", 3}, "gDY6"},
{{"\142\350\267", 3}, "Yui3"},
{{"\033\000\166", 3}, "GwB2"},
{{"\210\055\077", 3}, "iC0/"},
{{"\341\037\124", 3}, "4R9U"},
{{"\161\103\152", 3}, "cUNq"},
{{"\270\142\131", 3}, "uGJZ"},
{{"\337\076\074", 3}, "3z48"},
{{"\375\106\362", 3}, "/Uby"},
{{"\227\301\127", 3}, "l8FX"},
{{"\340\002\234", 3}, "4AKc"},
{{"\121\064\033", 3}, "UTQb"},
{{"\157\134\143", 3}, "b1xj"},
{{"\247\055\327", 3}, "py3X"},
{{"\340\142\005", 3}, "4GIF"},
{{"\060\260\143", 3}, "MLBj"},
{{"\075\203\170", 3}, "PYN4"},
{{"\143\160\016", 3}, "Y3AO"},
{{"\313\013\063", 3}, "ywsz"},
{{"\174\236\135", 3}, "fJ5d"},
{{"\103\047\026", 3}, "QycW"},
{{"\365\005\343", 3}, "9QXj"},
{{"\271\160\223", 3}, "uXCT"},
{{"\362\255\172", 3}, "8q16"},
{{"\113\012\015", 3}, "SwoN"},
// various lengths, generated by this python script:
//
// from std::string import lowercase as lc
// for i in range(27):
// print '{ %2d, "%s",%s "%s" },' % (i, lc[:i], ' ' * (26-i),
// lc[:i].encode('base64').strip())
{{"", 0}, {"", 0}},
{"a", "YQ=="},
{"ab", "YWI="},
{"abc", "YWJj"},
{"abcd", "YWJjZA=="},
{"abcde", "YWJjZGU="},
{"abcdef", "YWJjZGVm"},
{"abcdefg", "YWJjZGVmZw=="},
{"abcdefgh", "YWJjZGVmZ2g="},
{"abcdefghi", "YWJjZGVmZ2hp"},
{"abcdefghij", "YWJjZGVmZ2hpag=="},
{"abcdefghijk", "YWJjZGVmZ2hpams="},
{"abcdefghijkl", "YWJjZGVmZ2hpamts"},
{"abcdefghijklm", "YWJjZGVmZ2hpamtsbQ=="},
{"abcdefghijklmn", "YWJjZGVmZ2hpamtsbW4="},
{"abcdefghijklmno", "YWJjZGVmZ2hpamtsbW5v"},
{"abcdefghijklmnop", "YWJjZGVmZ2hpamtsbW5vcA=="},
{"abcdefghijklmnopq", "YWJjZGVmZ2hpamtsbW5vcHE="},
{"abcdefghijklmnopqr", "YWJjZGVmZ2hpamtsbW5vcHFy"},
{"abcdefghijklmnopqrs", "YWJjZGVmZ2hpamtsbW5vcHFycw=="},
{"abcdefghijklmnopqrst", "YWJjZGVmZ2hpamtsbW5vcHFyc3Q="},
{"abcdefghijklmnopqrstu", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1"},
{"abcdefghijklmnopqrstuv", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dg=="},
{"abcdefghijklmnopqrstuvw", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnc="},
{"abcdefghijklmnopqrstuvwx", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4"},
{"abcdefghijklmnopqrstuvwxy", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eQ=="},
{"abcdefghijklmnopqrstuvwxyz", "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo="},
};
template <typename StringType>
void TestEscapeAndUnescape() {
// Check the short strings; this tests the math (and boundaries)
for (const auto& tc : base64_tests) {
// Test plain base64.
StringType encoded("this junk should be ignored");
absl::Base64Escape(tc.plaintext, &encoded);
EXPECT_EQ(encoded, tc.cyphertext);
EXPECT_EQ(absl::Base64Escape(tc.plaintext), tc.cyphertext);
StringType decoded("this junk should be ignored");
EXPECT_TRUE(absl::Base64Unescape(encoded, &decoded));
EXPECT_EQ(decoded, tc.plaintext);
StringType websafe_with_padding(tc.cyphertext);
for (unsigned int c = 0; c < websafe_with_padding.size(); ++c) {
if ('+' == websafe_with_padding[c]) websafe_with_padding[c] = '-';
if ('/' == websafe_with_padding[c]) websafe_with_padding[c] = '_';
// Intentionally keeping padding aka '='.
}
// Test plain websafe (aka without padding).
StringType websafe(websafe_with_padding);
for (unsigned int c = 0; c < websafe.size(); ++c) {
if ('=' == websafe[c]) {
websafe.resize(c);
break;
}
}
encoded = "this junk should be ignored";
absl::WebSafeBase64Escape(tc.plaintext, &encoded);
EXPECT_EQ(encoded, websafe);
EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), websafe);
decoded = "this junk should be ignored";
EXPECT_TRUE(absl::WebSafeBase64Unescape(websafe, &decoded));
EXPECT_EQ(decoded, tc.plaintext);
}
// Now try the long strings, this tests the streaming
for (const auto& tc : absl::strings_internal::base64_strings()) {
StringType buffer;
absl::WebSafeBase64Escape(tc.plaintext, &buffer);
EXPECT_EQ(tc.cyphertext, buffer);
EXPECT_EQ(absl::WebSafeBase64Escape(tc.plaintext), tc.cyphertext);
}
// Verify the behavior when decoding bad data
{
absl::string_view data_set[] = {"ab-/", absl::string_view("\0bcd", 4),
absl::string_view("abc.\0", 5)};
for (absl::string_view bad_data : data_set) {
StringType buf;
EXPECT_FALSE(absl::Base64Unescape(bad_data, &buf));
EXPECT_FALSE(absl::WebSafeBase64Unescape(bad_data, &buf));
EXPECT_TRUE(buf.empty());
}
}
}
TEST(Base64, EscapeAndUnescape) {
TestEscapeAndUnescape<std::string>();
}
TEST(Base64, Padding) {
// Padding is optional.
// '.' is an acceptable padding character, just like '='.
std::initializer_list<absl::string_view> good_padding = {
"YQ",
"YQ==",
"YQ=.",
"YQ.=",
"YQ..",
};
for (absl::string_view b64 : good_padding) {
std::string decoded;
EXPECT_TRUE(absl::Base64Unescape(b64, &decoded));
EXPECT_EQ(decoded, "a");
std::string websafe_decoded;
EXPECT_TRUE(absl::WebSafeBase64Unescape(b64, &websafe_decoded));
EXPECT_EQ(websafe_decoded, "a");
}
std::initializer_list<absl::string_view> bad_padding = {
"YQ=",
"YQ.",
"YQ===",
"YQ==.",
"YQ=.=",
"YQ=..",
"YQ.==",
"YQ.=.",
"YQ..=",
"YQ...",
"YQ====",
"YQ....",
"YQ=====",
"YQ.....",
};
for (absl::string_view b64 : bad_padding) {
std::string decoded;
EXPECT_FALSE(absl::Base64Unescape(b64, &decoded));
std::string websafe_decoded;
EXPECT_FALSE(absl::WebSafeBase64Unescape(b64, &websafe_decoded));
}
}
TEST(Base64, DISABLED_HugeData) {
const size_t kSize = size_t(3) * 1000 * 1000 * 1000;
static_assert(kSize % 3 == 0, "kSize must be divisible by 3");
const std::string huge(kSize, 'x');
std::string escaped;
absl::Base64Escape(huge, &escaped);
// Generates the string that should match a base64 encoded "xxx..." string.
// "xxx" in base64 is "eHh4".
std::string expected_encoding;
expected_encoding.reserve(kSize / 3 * 4);
for (size_t i = 0; i < kSize / 3; ++i) {
expected_encoding.append("eHh4");
}
EXPECT_EQ(expected_encoding, escaped);
std::string unescaped;
EXPECT_TRUE(absl::Base64Unescape(escaped, &unescaped));
EXPECT_EQ(huge, unescaped);
}
TEST(Escaping, HexStringToBytesBackToHex) {
std::string bytes, hex;
constexpr absl::string_view kTestHexLower = "1c2f0032f40123456789abcdef";
constexpr absl::string_view kTestHexUpper = "1C2F0032F40123456789ABCDEF";
constexpr absl::string_view kTestBytes = absl::string_view(
"\x1c\x2f\x00\x32\xf4\x01\x23\x45\x67\x89\xab\xcd\xef", 13);
EXPECT_TRUE(absl::HexStringToBytes(kTestHexLower, &bytes));
EXPECT_EQ(bytes, kTestBytes);
EXPECT_TRUE(absl::HexStringToBytes(kTestHexUpper, &bytes));
EXPECT_EQ(bytes, kTestBytes);
hex = absl::BytesToHexString(kTestBytes);
EXPECT_EQ(hex, kTestHexLower);
// Same buffer.
// We do not care if this works since we do not promise it in the contract.
// The purpose of this test is to to see if the program will crash or if
// sanitizers will catch anything.
bytes = std::string(kTestHexUpper);
(void)absl::HexStringToBytes(bytes, &bytes);
// Length not a multiple of two.
EXPECT_FALSE(absl::HexStringToBytes("1c2f003", &bytes));
// Not hex.
EXPECT_FALSE(absl::HexStringToBytes("1c2f00ft", &bytes));
// Empty input.
bytes = "abc";
EXPECT_TRUE(absl::HexStringToBytes("", &bytes));
EXPECT_EQ("", bytes); // Results in empty output.
}
TEST(HexAndBack, HexStringToBytes_and_BytesToHexString) {
std::string hex_mixed = "0123456789abcdefABCDEF";
std::string bytes_expected = "\x01\x23\x45\x67\x89\xab\xcd\xef\xAB\xCD\xEF";
std::string hex_only_lower = "0123456789abcdefabcdef";
std::string bytes_result = absl::HexStringToBytes(hex_mixed);
EXPECT_EQ(bytes_expected, bytes_result);
std::string prefix_valid = hex_mixed + "?";
std::string prefix_valid_result = absl::HexStringToBytes(
absl::string_view(prefix_valid.data(), prefix_valid.size() - 1));
EXPECT_EQ(bytes_expected, prefix_valid_result);
std::string infix_valid = "?" + hex_mixed + "???";
std::string infix_valid_result = absl::HexStringToBytes(
absl::string_view(infix_valid.data() + 1, hex_mixed.size()));
EXPECT_EQ(bytes_expected, infix_valid_result);
std::string hex_result = absl::BytesToHexString(bytes_expected);
EXPECT_EQ(hex_only_lower, hex_result);
}
} // namespace