| /* |
| * 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. |
| */ |
| |
| #include <stdint.h> |
| |
| #include <limits> |
| #include <regex> |
| |
| #include <gtest/gtest.h> |
| |
| #include <geode/CacheFactory.hpp> |
| #include <geode/DataOutput.hpp> |
| |
| #include "ByteArrayFixture.hpp" |
| #include "DataInputInternal.hpp" |
| #include "DataOutputInternal.hpp" |
| #include "SerializationRegistry.hpp" |
| #include "gtest_extensions.h" |
| |
| namespace { |
| |
| using apache::geode::client::ByteArray; |
| using apache::geode::client::CacheableString; |
| using apache::geode::client::DataInputInternal; |
| using apache::geode::client::DataOutput; |
| using apache::geode::client::DataOutputInternal; |
| using apache::geode::client::SerializationRegistry; |
| |
| class TestDataOutput : public DataOutputInternal { |
| public: |
| TestDataOutput() |
| : DataOutputInternal(), m_byteArray(nullptr), m_serializationRegistry() { |
| // NOP |
| } |
| |
| ~TestDataOutput() noexcept override { |
| delete m_byteArray; |
| m_byteArray = nullptr; |
| } |
| |
| const ByteArray& getByteArray() const { |
| if (!m_byteArray) { |
| m_byteArray = new ByteArray(getBuffer(), getBufferLength()); |
| } |
| return *m_byteArray; |
| } |
| |
| protected: |
| virtual const SerializationRegistry& getSerializationRegistry() |
| const override { |
| return m_serializationRegistry; |
| } |
| |
| private: |
| mutable ByteArray* m_byteArray; |
| SerializationRegistry m_serializationRegistry; |
| }; |
| |
| class CacheableStringTests : public ::testing::Test, public ByteArrayFixture { |
| public: |
| protected: |
| }; |
| |
| inline std::string to_hex(const uint8_t* bytes, size_t len) { |
| std::stringstream ss; |
| ss << std::setfill('0') << std::hex; |
| for (size_t i(0); i < len; ++i) { |
| ss << std::setw(2) << static_cast<int>(bytes[i]); |
| } |
| return ss.str(); |
| } |
| |
| inline std::string to_hex(const DataOutput& out) { |
| return to_hex(out.getBuffer(), out.getBufferLength()); |
| } |
| |
| TEST_F(CacheableStringTests, CreateFromStdStringRValue) { |
| auto s = std::string("You had me at meat tornado."); |
| auto m = s; |
| auto c = CacheableString::create(std::move(m)); |
| |
| ASSERT_TRUE(m.empty()); |
| ASSERT_EQ(s, c->value()); |
| } |
| |
| TEST_F(CacheableStringTests, CreateFromStdStringLValue) { |
| auto s = std::string("You had me at meat tornado."); |
| auto c = CacheableString::create(s); |
| |
| ASSERT_FALSE(s.empty()); |
| ASSERT_EQ(s, c->value()); |
| } |
| |
| TEST_F(CacheableStringTests, TestToDataAscii) { |
| auto origStr = CacheableString::create("You had me at meat tornado."); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| EXPECT_EQ("001b596f7520686164206d65206174206d65617420746f726e61646f2e", |
| to_hex(out)); |
| } |
| |
| TEST_F(CacheableStringTests, TestFromDataAscii) { |
| std::string utf8("You had me at meat tornado."); |
| auto origStr = CacheableString::create(utf8.c_str()); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| auto str = std::dynamic_pointer_cast<CacheableString>( |
| CacheableString::createUTFDeserializable()); |
| DataInputInternal in(out.getBuffer(), out.getBufferLength()); |
| str->fromData(in); |
| |
| EXPECT_EQ(utf8, str->value()); |
| } |
| |
| TEST_F(CacheableStringTests, TestToDataNonAscii) { |
| auto origStr = CacheableString::create(u8"You had me at meat tornad\u00F6."); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| EXPECT_EQ("001c596f7520686164206d65206174206d65617420746f726e6164c3b62e", |
| to_hex(out)); |
| } |
| |
| TEST_F(CacheableStringTests, TestFromDataNonAscii) { |
| std::string utf8(u8"You had me at meat tornad\u00F6."); |
| auto origStr = CacheableString::create(utf8); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| auto str = std::dynamic_pointer_cast<CacheableString>( |
| CacheableString::createUTFDeserializable()); |
| DataInputInternal in(out.getBuffer(), out.getBufferLength()); |
| str->fromData(in); |
| |
| EXPECT_EQ(utf8, str->value()); |
| } |
| |
| TEST_F(CacheableStringTests, TestToDataAsciiHuge) { |
| size_t originalLen = std::numeric_limits<uint16_t>::max(); |
| std::string utf8(originalLen, 'a'); |
| utf8.append("a"); |
| originalLen++; |
| |
| auto origStr = CacheableString::create(utf8.c_str()); |
| TestDataOutput out; |
| origStr->toData(out); |
| |
| auto&& bufLen = originalLen + 4; // strLen (unit32_t) |
| EXPECT_EQ(bufLen, out.getBufferLength()); |
| |
| // 0x00010000 - length |
| // 0x61 - first 'a' |
| // EXPECT_MATCH("0001000061.*61", to_hex(out)); |
| EXPECT_BYTEARRAY_EQ("0001000061\\h{131068}61", out.getByteArray()); |
| } |
| |
| TEST_F(CacheableStringTests, TestFromDataAsciiHuge) { |
| std::string utf8(std::numeric_limits<uint16_t>::max(), 'a'); |
| utf8.append("a"); |
| |
| auto origStr = CacheableString::create(utf8.c_str()); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| auto str = std::dynamic_pointer_cast<CacheableString>( |
| CacheableString::createDeserializableHuge()); |
| DataInputInternal in(out.getBuffer(), out.getBufferLength()); |
| str->fromData(in); |
| |
| EXPECT_EQ(utf8, str->value()); |
| } |
| |
| TEST_F(CacheableStringTests, TestToDataNonAsciiHuge) { |
| std::string utf8(std::numeric_limits<uint16_t>::max(), 'a'); |
| utf8.append(u8"\u00E4"); |
| auto&& utf16 = |
| std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>, wchar_t>{} |
| .from_bytes(utf8); |
| |
| auto origStr = CacheableString::create(utf8.c_str()); |
| TestDataOutput out; |
| origStr->toData(out); |
| |
| // utf-16 units length (unit32_t) + utf-16 units (uint16_t*) |
| auto&& bufLen = 4 + utf16.length() * sizeof(uint16_t); |
| EXPECT_EQ(bufLen, out.getBufferLength()); |
| |
| // 0x00010000 - length |
| // 0x0061 - first 'a' |
| // 0x006100E4 - last 'a\u00e4' |
| // EXPECT_MATCH("000100000061.*006100e4", to_hex(out)); |
| EXPECT_BYTEARRAY_EQ("000100000061\\h{262132}006100E4", out.getByteArray()); |
| } |
| |
| TEST_F(CacheableStringTests, TestFromDataNonAsciiHuge) { |
| std::string utf8(std::numeric_limits<uint16_t>::max(), 'a'); |
| utf8.append(u8"\u00E4"); |
| |
| auto origStr = CacheableString::create(utf8); |
| DataOutputInternal out; |
| origStr->toData(out); |
| |
| auto str = std::dynamic_pointer_cast<CacheableString>( |
| CacheableString::createUTFDeserializableHuge()); |
| DataInputInternal in(out.getBuffer(), out.getBufferLength()); |
| str->fromData(in); |
| |
| EXPECT_EQ(utf8, str->value()); |
| } |
| |
| } // namespace |