blob: 663672b4a60e0ccebb6f1b8273c821e88058760c [file] [log] [blame]
/*
* Copyright 2012 Google Inc.
*
* 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
*
* 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.
*/
// Author: jmarantz@google.com (Joshua Marantz)
#include "pagespeed/kernel/base/shared_string.h"
#include "pagespeed/kernel/base/gtest.h"
#include "pagespeed/kernel/base/string.h"
namespace net_instaweb {
class SharedStringTest : public testing::Test {
protected:
};
TEST_F(SharedStringTest, ConstructFromStringPiece) {
SharedString ss(StringPiece("hello"));
EXPECT_STREQ("hello", ss.Value());
}
TEST_F(SharedStringTest, ConstructFromString) {
SharedString ss(GoogleString("hello"));
EXPECT_STREQ("hello", ss.Value());
}
TEST_F(SharedStringTest, ConstructFromCharStar) {
SharedString ss("hello");
EXPECT_STREQ("hello", ss.Value());
}
TEST_F(SharedStringTest, ConstructFromSharedString) {
SharedString ss("hello");
EXPECT_TRUE(ss.unique());
SharedString ss2(ss);
EXPECT_FALSE(ss.unique());
EXPECT_FALSE(ss2.unique());
EXPECT_STREQ("hello", ss.Value());
EXPECT_STREQ("hello", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2))<< "storage is shared";
EXPECT_FALSE(ss.trimmed());
// Mutations to ss do not affect ss2.
ss.Append(", World!");
EXPECT_STREQ("hello, World!", ss.Value());
EXPECT_STREQ("hello", ss2.Value()) << "ss2 unaffected by ss.Append";
EXPECT_TRUE(ss.SharesStorage(ss2)) << "storage is still shared!";
EXPECT_FALSE(ss.trimmed());
// Removing a suffix means that we no longer have an accurate
// GoogleString representation, and trimmed() return false.
ss.RemoveSuffix(1); // removes "!"
EXPECT_STREQ("hello, World", ss.Value());
EXPECT_TRUE(ss.trimmed());
// In order to Append on more bytes, we must detach first so that
// we don't have a suffix.
ss.DetachRetainingContent();
ss.Append(".");
EXPECT_STREQ("hello, World.", ss.Value());
EXPECT_FALSE(ss.trimmed());
// Now re-link the two SharedStrings.
ss2 = ss;
EXPECT_STREQ("hello, World.", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2)) << "storage is shared!";
// Prefix removal is also not shared, although the storage is still linked.
ss.RemovePrefix(7); // removes "hello, "
EXPECT_STREQ("World.", ss.Value());
EXPECT_STREQ("hello, World.", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2)) << "storage is shared!";
EXPECT_FALSE(ss.unique());
EXPECT_FALSE(ss2.unique());
}
TEST_F(SharedStringTest, Assign) {
SharedString ss("hello");
SharedString ss2(ss);
ss.Assign("Goodbye");
EXPECT_STREQ("Goodbye", ss.Value());
EXPECT_STREQ("hello", ss2.Value()); // Detach on assign.
// It's OK to assign from overlapping bytes.
ss.Assign(ss.Value().substr(4));
EXPECT_STREQ("bye", ss.Value());
}
TEST_F(SharedStringTest, SwapWithString) {
SharedString ss("hello");
GoogleString buf("Goodbye");
ss.SwapWithString(&buf);
EXPECT_STREQ("Goodbye", ss.Value());
EXPECT_STREQ("hello", buf);
ss.RemoveSuffix(1);
ss.RemovePrefix(4);
EXPECT_STREQ("by", ss.Value());
ss.SwapWithString(&buf);
EXPECT_STREQ("hello", ss.Value()) << "1 byte of suffix no longer removed.";
EXPECT_STREQ("Goodbye", buf) << "string storage intact after removing prefix"
<< " and truncating.";
SharedString ss2 = ss;
ss.SwapWithString(&buf);
EXPECT_STREQ("Goodbye", ss.Value());
EXPECT_STREQ("", buf) << "due to ss being detached as part of the swap.";
EXPECT_STREQ("hello", ss2.Value()) << "detached.";
}
TEST_F(SharedStringTest, Clear) {
SharedString ss("hello");
ss.DetachAndClear();
EXPECT_TRUE(ss.empty());
EXPECT_EQ(0, ss.size());
EXPECT_STREQ("", ss.Value());
// When we remove a prefix, Clear clears that fact too.
ss.Assign("12345");
ss.RemovePrefix(1);
EXPECT_STREQ("2345", ss.Value());
ss.DetachAndClear();
EXPECT_EQ(0, ss.size());
ss.Assign("12345");
EXPECT_STREQ("12345", ss.Value());
// When a string is shared, clearing it has no effect.
SharedString ss2(ss);
ss2.RemoveSuffix(2);
EXPECT_STREQ("12345", ss.Value()); // Does not have its suffix removed.
EXPECT_STREQ("123", ss2.Value());
ss.DetachAndClear();
EXPECT_STREQ("", ss.Value());
EXPECT_STREQ("123", ss2.Value());
EXPECT_TRUE(ss2.unique());
}
TEST_F(SharedStringTest, DetachRetainingContent) {
SharedString ss("hello");
SharedString ss2 = ss;
EXPECT_TRUE(ss.SharesStorage(ss2));
ss.DetachRetainingContent();
EXPECT_FALSE(ss.SharesStorage(ss2));
EXPECT_TRUE(ss.unique());
EXPECT_STREQ("hello", ss.Value());
EXPECT_STREQ("hello", ss2.Value());
}
TEST_F(SharedStringTest, WriteAt) {
SharedString ss("HELLO");
SharedString ss2 = ss;
ss.WriteAt(0, "123", 3);
EXPECT_STREQ("123LO", ss.Value());
ss.WriteAt(1, "YZ", 2);
EXPECT_STREQ("1YZLO", ss.Value());
EXPECT_STREQ("1YZLO", ss2.Value());
// Now trim some characters and make sure this all stays sane.
ss.RemovePrefix(1);
ss.RemoveSuffix(1);
EXPECT_STREQ("YZL", ss.Value());
EXPECT_STREQ("1YZLO", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2)); // storage still shared.
// Replace the Z with an A. This affects both ss and ss2.
ss.WriteAt(1, "A", 1);
EXPECT_STREQ("YAL", ss.Value());
EXPECT_STREQ("1YALO", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2)); // storage still shared.
}
TEST_F(SharedStringTest, Extend) {
SharedString ss("x");
SharedString ss2 = ss;
ss.Extend(4); // adds 3 undefined characters.
ss.WriteAt(1, "123", 3);
EXPECT_STREQ("x123", ss.Value());
EXPECT_STREQ("x", ss2.Value()) << "ss2 was not extended";
EXPECT_TRUE(ss.SharesStorage(ss2)) << "but ss and ss2 still share storage";
// It's OK to extend a string that has a removed prefix, and storage
// sharing will still be retained.
ss.RemovePrefix(1);
EXPECT_STREQ("123", ss.Value());
EXPECT_STREQ("x", ss2.Value());
ss.Extend(4); // adds one more blank.
ss.WriteAt(3, "4", 1);
EXPECT_STREQ("1234", ss.Value());
EXPECT_STREQ("x", ss2.Value()) << "ss2 still not affected";
EXPECT_TRUE(ss.SharesStorage(ss2)) << "and storage is still retained";
ss.RemoveSuffix(2);
EXPECT_STREQ("12", ss.Value());
EXPECT_STREQ("x", ss2.Value());
EXPECT_TRUE(ss.SharesStorage(ss2)) << "and storage is still retained";
// It's also fine to extend a truncated string, but then it gets its
// own storage.
ss.Extend(7);
ss.WriteAt(2, "abcde", 5);
EXPECT_STREQ("12abcde", ss.Value());
EXPECT_STREQ("x", ss2.Value()) << "ss2 still unaffected";
EXPECT_FALSE(ss.SharesStorage(ss2))<< "finally storage is detached";
}
TEST_F(SharedStringTest, ExtendUniqueTruncated) {
SharedString ss("abc");
const GoogleString* original_storage = ss.StringValue();
ss.RemoveSuffix(1);
ss.Extend(6);
ss.WriteAt(2, "1234", 4);
EXPECT_EQ("ab1234", ss.Value());
EXPECT_EQ(original_storage, ss.StringValue())
<< "Re-use the same storage across truncate/extend of unique string";
}
} // namespace net_instaweb