blob: 1be014807adf9faa05d66f812402c606d546f95c [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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
*
* http://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.
*/
// Unit-test the base64 encoder.
#include "pagespeed/kernel/base/basictypes.h"
#include "pagespeed/kernel/base/base64_util.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/string.h"
#include "pagespeed/kernel/base/string_util.h"
namespace {
const char chinese_data[] = "中华网,中华,中国,中文网,中国新闻,香港新闻,"
"国际新闻,中文新闻,新闻,港台新闻,两会,嫦娥一号";
const int chinese_size = STATIC_STRLEN(chinese_data);
// Also test some binary data, including embedded nulls, 2^7-1, 2^8-1
const char binary_data[] = "\0\1\2\3\4\5\6\7\10\0\177\176\175\377\376";
const int binary_size = STATIC_STRLEN(binary_data);
class Codec {
public:
virtual ~Codec() {}
virtual void encode(const GoogleString& in, GoogleString* out) const = 0;
virtual bool decode(const GoogleString& in, GoogleString* out) const = 0;
};
class WebSafeBase64Codec : public Codec {
public:
virtual void encode(const GoogleString& in, GoogleString* out) const {
net_instaweb::Web64Encode(in, out);
}
virtual bool decode(const GoogleString& in, GoogleString* out) const {
return net_instaweb::Web64Decode(in, out);
}
};
class MimeBase64Codec : public Codec {
public:
virtual void encode(const GoogleString& in, GoogleString* out) const {
net_instaweb::Mime64Encode(in, out);
}
virtual bool decode(const GoogleString& in, GoogleString* out) const {
return net_instaweb::Mime64Decode(in, out);
}
};
} // namespace
namespace net_instaweb {
class Base64Test : public testing::Test {
protected:
Base64Test()
: chinese_(chinese_data, chinese_size),
binary_(binary_data, binary_size),
web64_codec_(),
mime64_codec_() {
}
void TestWeb64(const Codec &codec, const GoogleString& input) {
GoogleString encoded, decoded;
codec.encode(input, &encoded);
ASSERT_TRUE(codec.decode(encoded, &decoded));
EXPECT_EQ(input, decoded);
}
// Tests that attempts to decode a string that is not properly base64
// encoded will gracefully fail (Web64Decode returns false) rather than
// crash or produce invalid output. corrupt_char must be a character
// that is not in the base64 char-set.
//
// If the 'index' is specified as a negative number, it will be taken
// as an offset from the end of the string.
void TestCorrupt(const Codec &codec,
const GoogleString& input, char corrupt_char, int index) {
GoogleString encoded, decoded;
codec.encode(input, &encoded);
if (index < 0) {
index = encoded.size() + index;
}
encoded[index] = corrupt_char;
ASSERT_FALSE(codec.decode(encoded, &decoded));
}
GoogleString chinese_;
GoogleString binary_;
WebSafeBase64Codec web64_codec_;
MimeBase64Codec mime64_codec_;
private:
DISALLOW_COPY_AND_ASSIGN(Base64Test);
};
TEST_F(Base64Test, Chinese) {
TestWeb64(web64_codec_, chinese_);
TestWeb64(mime64_codec_, chinese_);
}
TEST_F(Base64Test, Binary) {
TestWeb64(web64_codec_, binary_);
TestWeb64(mime64_codec_, binary_);
}
TEST_F(Base64Test, CorruptFirst) {
TestCorrupt(web64_codec_, chinese_, '@', 0);
TestCorrupt(mime64_codec_, chinese_, '@', 0);
}
TEST_F(Base64Test, CorruptMiddle) {
TestCorrupt(web64_codec_, chinese_, ':', chinese_.size() / 2);
TestCorrupt(mime64_codec_, chinese_, ':', chinese_.size() / 2);
}
TEST_F(Base64Test, CorruptEnd) {
// I wanted to put the '/' as the last character, but it turns out
// that encoders may put '=' characters in to pad to a multiple of
// 4 bytes, and the decoder stops decoding when it gets to the first
// pad character, so changing "==" to "=/" has no effect.
TestCorrupt(web64_codec_, chinese_, '/', -4);
TestCorrupt(mime64_codec_, chinese_, '_', -4);
}
} // namespace net_instaweb