From 5a0fccc9cfab617e4eb239ae9a30b8d8b1a48277 Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Fri, 3 Jul 2015 13:29:31 -0700 Subject: [PATCH] Add unit tests for StringValue and Value. Add unit test framework to CMakeLists.txt. Fix a couple of bugs found by unit tests. Change-Id: I2092a7f0570fae0f19f9e083c4837ccefcc4ca1a --- CMakeLists.txt | 50 ++++- src/Value.cpp | 23 +-- src/Value.h | 16 +- test/CMakeLists.txt | 1 + test/unit/CMakeLists.txt | 10 + test/unit/main.cpp | 8 + test/unit/test_StringValue.cpp | 52 +++++ test/unit/test_Value.cpp | 347 +++++++++++++++++++++++++++++++++ 8 files changed, 479 insertions(+), 28 deletions(-) create mode 100644 test/CMakeLists.txt create mode 100644 test/unit/CMakeLists.txt create mode 100644 test/unit/main.cpp create mode 100644 test/unit/test_StringValue.cpp create mode 100644 test/unit/test_Value.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 832bd8637e..9ae404710e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,5 +1,5 @@ cmake_minimum_required(VERSION 2.8) -project(NetworkTables) +project(ntcore) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++1y -Wformat=2 -Wall -Wextra -Werror -pedantic -Wno-unused-parameter") @@ -10,6 +10,48 @@ add_library(ntcore STATIC ${SRC_SHARE_FILES} ${SRC_TARGET_FILES}) target_link_libraries(ntcore) INSTALL(TARGETS ntcore ARCHIVE DESTINATION lib COMPONENT lib) INSTALL(DIRECTORY include DESTINATION ${CMAKE_INSTALL_PREFIX} COMPONENT headers) -# lib/ c gcc_s ld-linux -# usr/lib stdc++ -# NiRioSrv + + +# +# +# + +# We need thread support +find_package(Threads REQUIRED) + +# Enable ExternalProject CMake module +include(ExternalProject) + +# Download and install GoogleMock +ExternalProject_Add( + gmock + URL https://googlemock.googlecode.com/files/gmock-1.7.0.zip + PREFIX ${CMAKE_CURRENT_BINARY_DIR}/gmock + # Disable install step + INSTALL_COMMAND "" +) + +# Create a libgmock target to be used as a dependency by test programs +add_library(libgmock IMPORTED STATIC GLOBAL) +add_dependencies(libgmock gmock) +add_library(libgtest IMPORTED STATIC GLOBAL) +add_dependencies(libgtest gmock) + +# Set gmock properties +ExternalProject_Get_Property(gmock source_dir binary_dir) +set_target_properties(libgmock PROPERTIES + "IMPORTED_LOCATION" "${binary_dir}/libgmock.a" + "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}" +# "INTERFACE_INCLUDE_DIRECTORIES" "${source_dir}/include" +) +set_target_properties(libgtest PROPERTIES + "IMPORTED_LOCATION" "${binary_dir}/gtest/libgtest.a" + "IMPORTED_LINK_INTERFACE_LIBRARIES" "${CMAKE_THREAD_LIBS_INIT}" +# "INTERFACE_INCLUDE_DIRECTORIES" "${source_dir}/include" +) +# I couldn't make it work with INTERFACE_INCLUDE_DIRECTORIES +include_directories("${source_dir}/include") +include_directories("${source_dir}/gtest/include") + +add_subdirectory(test) + diff --git a/src/Value.cpp b/src/Value.cpp index 4717da0c73..4ebb5dd64d 100644 --- a/src/Value.cpp +++ b/src/Value.cpp @@ -40,24 +40,6 @@ void Value::SetBooleanArray(llvm::ArrayRef value) { std::copy(value.begin(), value.end(), data.arr_boolean.arr); } -void Value::SetBooleanArray(llvm::ArrayRef value) { - // handle type change - if (NT_Value::type != NT_BOOLEAN_ARRAY) { - NT_DisposeValue(this); - data.arr_boolean.arr = nullptr; - data.arr_boolean.size = 0; // set to zero so size change is hit below - NT_Value::type = NT_BOOLEAN_ARRAY; - } - // handle size change - if (data.arr_boolean.size != value.size()) { - std::free(data.arr_boolean.arr); - data.arr_boolean.arr = - static_cast(std::malloc(value.size()*sizeof(int))); - data.arr_boolean.size = value.size(); - } - std::copy(value.begin(), value.end(), data.arr_boolean.arr); -} - void Value::SetDoubleArray(llvm::ArrayRef value) { // handle type change if (NT_Value::type != NT_DOUBLE_ARRAY) { @@ -89,6 +71,11 @@ void Value::SetStringArray(std::vector& value) { std::free(data.arr_string.arr); data.arr_string.arr = static_cast(std::malloc(value.size()*sizeof(NT_String))); + // need to initialize array to avoid invalid frees in std::move below. + for (size_t i=0; i(data.v_raw); } - // Ideally this would return llvm::ArrayRef but the C headers must - // use "int" and casting may be very unsafe. llvm::ArrayRef GetBooleanArray() const { assert(NT_Value::type == NT_BOOLEAN_ARRAY); return llvm::ArrayRef(data.arr_boolean.arr, data.arr_boolean.size); @@ -97,7 +99,7 @@ class Value : private NT_Value { return llvm::ArrayRef(data.arr_double.arr, data.arr_double.size); } llvm::ArrayRef GetStringArray() const { - assert(NT_Value::type == NT_BOOLEAN_ARRAY); + assert(NT_Value::type == NT_STRING_ARRAY); return llvm::ArrayRef( static_cast(data.arr_string.arr), data.arr_string.size); } @@ -127,7 +129,7 @@ class Value : private NT_Value { data.v_string.len = 0; NT_Value::type = NT_STRING; } - data.v_string = value; + static_cast(data.v_string) = std::move(value); } void SetRaw(llvm::StringRef value) { SetRaw(StringValue(value)); } void SetRaw(StringValue&& value) { @@ -137,11 +139,10 @@ class Value : private NT_Value { data.v_raw.len = 0; NT_Value::type = NT_RAW; } - data.v_raw = value; + static_cast(data.v_raw) = std::move(value); } void SetBooleanArray(llvm::ArrayRef value); - void SetBooleanArray(llvm::ArrayRef value); void SetDoubleArray(llvm::ArrayRef value); // Note: This function moves the values out of the vector. @@ -174,6 +175,9 @@ class Value : private NT_Value { }; bool operator==(const Value& lhs, const Value& rhs); +inline bool operator!=(const Value& lhs, const Value& rhs) { + return !(lhs == rhs); +} } // namespace ntimpl diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000000..269aea0c60 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(unit) diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt new file mode 100644 index 0000000000..7d97a89931 --- /dev/null +++ b/test/unit/CMakeLists.txt @@ -0,0 +1,10 @@ +file(GLOB SRCS *.cpp) + +add_executable(testntcore ${SRCS}) + +target_link_libraries(testntcore + ntcore + libgmock + libgtest + ) + diff --git a/test/unit/main.cpp b/test/unit/main.cpp new file mode 100644 index 0000000000..419b71c52d --- /dev/null +++ b/test/unit/main.cpp @@ -0,0 +1,8 @@ +#include "gtest/gtest.h" + +int main(int argc, char **argv) +{ + ::testing::InitGoogleTest(&argc, argv); + int ret = RUN_ALL_TESTS(); + return ret; +} diff --git a/test/unit/test_StringValue.cpp b/test/unit/test_StringValue.cpp new file mode 100644 index 0000000000..996d4aec88 --- /dev/null +++ b/test/unit/test_StringValue.cpp @@ -0,0 +1,52 @@ +#include "Value.h" + +#include "gtest/gtest.h" + +namespace ntimpl { + +class StringValueTest : public ::testing::Test { + public: + NT_String& ToNT(StringValue& v) { return v; } +}; + +TEST_F(StringValueTest, ConstructEmpty) { + StringValue v; + ASSERT_EQ(ToNT(v).str, nullptr); + ASSERT_EQ(ToNT(v).len, 0u); +} + +TEST_F(StringValueTest, ConstructStringRef) { + StringValue v("hello"); + ASSERT_EQ(llvm::StringRef(v), "hello"); + ASSERT_EQ(ToNT(v).str, llvm::StringRef("hello")); + ASSERT_EQ(ToNT(v).len, 5u); +} + +TEST_F(StringValueTest, ConstructMove) { + StringValue v("hello"); + char* orig = ToNT(v).str; + ASSERT_NE(orig, nullptr); + ASSERT_EQ(ToNT(v).len, 5u); + StringValue v2(std::move(v)); + ASSERT_EQ(ToNT(v).str, nullptr); + ASSERT_EQ(ToNT(v).len, 0u); + ASSERT_EQ(ToNT(v2).str, orig); + ASSERT_EQ(ToNT(v2).len, 5u); +} + +TEST_F(StringValueTest, AssignMove) { + StringValue v("hello"); + char* orig = ToNT(v).str; + ASSERT_NE(orig, nullptr); + ASSERT_EQ(ToNT(v).len, 5u); + StringValue v2("goodbye"); + ASSERT_NE(ToNT(v2).str, nullptr); + ASSERT_EQ(ToNT(v2).len, 7u); + v2 = std::move(v); + ASSERT_EQ(ToNT(v).str, nullptr); + ASSERT_EQ(ToNT(v).len, 0u); + ASSERT_EQ(ToNT(v2).str, orig); + ASSERT_EQ(ToNT(v2).len, 5u); +} + +} // namespace ntimpl diff --git a/test/unit/test_Value.cpp b/test/unit/test_Value.cpp new file mode 100644 index 0000000000..1a951f7922 --- /dev/null +++ b/test/unit/test_Value.cpp @@ -0,0 +1,347 @@ +#include "Value.h" + +#include "gtest/gtest.h" + +namespace ntimpl { + +class ValueTest : public ::testing::Test { + public: + NT_Value& ToNT(Value& v) { return v; } +}; + +typedef ValueTest ValueDeathTest; + +TEST_F(ValueTest, ConstructEmpty) { + Value v; + ASSERT_EQ(ToNT(v).type, NT_UNASSIGNED); + ASSERT_EQ(ToNT(v).last_change, 0u); + ASSERT_EQ(v.type(), NT_UNASSIGNED); +} + +TEST_F(ValueTest, ConstructMove) { + Value v; + v.SetString("hello"); + ASSERT_EQ(v.type(), NT_STRING); + Value v2(std::move(v)); + ASSERT_EQ(v.type(), NT_UNASSIGNED); + ASSERT_EQ(v2.type(), NT_STRING); +} + +TEST_F(ValueTest, AssignMove) { + Value v; + v.SetString("hello"); + ASSERT_EQ(v.type(), NT_STRING); + Value v2; + v2.SetDouble(0.5); + ASSERT_EQ(v2.type(), NT_DOUBLE); + v2 = std::move(v); + ASSERT_EQ(v.type(), NT_UNASSIGNED); + ASSERT_EQ(v2.type(), NT_STRING); +} + +TEST_F(ValueTest, Boolean) { + Value v; + v.SetBoolean(false); + ASSERT_EQ(ToNT(v).type, NT_BOOLEAN); + ASSERT_EQ(ToNT(v).data.v_boolean, false); + ASSERT_EQ(v.type(), NT_BOOLEAN); + ASSERT_EQ(v.GetBoolean(), false); + v.SetBoolean(true); + ASSERT_EQ(ToNT(v).type, NT_BOOLEAN); + ASSERT_EQ(ToNT(v).data.v_boolean, true); + ASSERT_EQ(v.type(), NT_BOOLEAN); + ASSERT_EQ(v.GetBoolean(), true); +} + +TEST_F(ValueTest, Double) { + Value v; + v.SetDouble(0.5); + ASSERT_EQ(ToNT(v).type, NT_DOUBLE); + ASSERT_EQ(ToNT(v).data.v_double, 0.5); + ASSERT_EQ(v.type(), NT_DOUBLE); + ASSERT_EQ(v.GetDouble(), 0.5); + v.SetDouble(0.25); + ASSERT_EQ(ToNT(v).type, NT_DOUBLE); + ASSERT_EQ(ToNT(v).data.v_double, 0.25); + ASSERT_EQ(v.type(), NT_DOUBLE); + ASSERT_EQ(v.GetDouble(), 0.25); +} + +TEST_F(ValueTest, String) { + Value v; + v.SetString("hello"); + ASSERT_EQ(ToNT(v).type, NT_STRING); + ASSERT_EQ(ToNT(v).data.v_string.str, llvm::StringRef("hello")); + ASSERT_EQ(ToNT(v).data.v_string.len, 5u); + ASSERT_EQ(v.type(), NT_STRING); + ASSERT_EQ(v.GetString(), "hello"); + v.SetString("goodbye"); + ASSERT_EQ(ToNT(v).type, NT_STRING); + ASSERT_EQ(ToNT(v).data.v_string.str, llvm::StringRef("goodbye")); + ASSERT_EQ(ToNT(v).data.v_string.len, 7u); + ASSERT_EQ(v.type(), NT_STRING); + ASSERT_EQ(v.GetString(), "goodbye"); +} + +TEST_F(ValueTest, Raw) { + Value v; + v.SetRaw("hello"); + ASSERT_EQ(ToNT(v).type, NT_RAW); + ASSERT_EQ(ToNT(v).data.v_string.str, llvm::StringRef("hello")); + ASSERT_EQ(ToNT(v).data.v_string.len, 5u); + ASSERT_EQ(v.type(), NT_RAW); + ASSERT_EQ(v.GetRaw(), "hello"); + v.SetRaw("goodbye"); + ASSERT_EQ(ToNT(v).type, NT_RAW); + ASSERT_EQ(ToNT(v).data.v_string.str, llvm::StringRef("goodbye")); + ASSERT_EQ(ToNT(v).data.v_string.len, 7u); + ASSERT_EQ(v.type(), NT_RAW); + ASSERT_EQ(v.GetRaw(), "goodbye"); +} + +TEST_F(ValueTest, BooleanArray) { + Value v; + std::vector vec{1,0,1}; + v.SetBooleanArray(vec); + ASSERT_EQ(ToNT(v).type, NT_BOOLEAN_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_boolean.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[1], vec[1]); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[2], vec[2]); + ASSERT_EQ(v.type(), NT_BOOLEAN_ARRAY); + ASSERT_EQ(v.GetBooleanArray(), llvm::ArrayRef(vec)); + + // assign with same size + vec = {0,1,0}; + v.SetBooleanArray(vec); + ASSERT_EQ(ToNT(v).type, NT_BOOLEAN_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_boolean.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[1], vec[1]); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[2], vec[2]); + ASSERT_EQ(v.type(), NT_BOOLEAN_ARRAY); + ASSERT_EQ(v.GetBooleanArray(), llvm::ArrayRef(vec)); + + // assign with different size + vec = {1,0}; + v.SetBooleanArray(vec); + ASSERT_EQ(ToNT(v).type, NT_BOOLEAN_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_boolean.size, 2u); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_boolean.arr[1], vec[1]); + ASSERT_EQ(v.type(), NT_BOOLEAN_ARRAY); + ASSERT_EQ(v.GetBooleanArray(), llvm::ArrayRef(vec)); +} + +TEST_F(ValueTest, DoubleArray) { + Value v; + std::vector vec{0.5,0.25,0.5}; + v.SetDoubleArray(vec); + ASSERT_EQ(ToNT(v).type, NT_DOUBLE_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_double.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_double.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_double.arr[1], vec[1]); + ASSERT_EQ(ToNT(v).data.arr_double.arr[2], vec[2]); + ASSERT_EQ(v.type(), NT_DOUBLE_ARRAY); + ASSERT_EQ(v.GetDoubleArray(), llvm::ArrayRef(vec)); + + // assign with same size + vec = {0.25,0.5,0.25}; + v.SetDoubleArray(vec); + ASSERT_EQ(ToNT(v).type, NT_DOUBLE_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_double.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_double.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_double.arr[1], vec[1]); + ASSERT_EQ(ToNT(v).data.arr_double.arr[2], vec[2]); + ASSERT_EQ(v.type(), NT_DOUBLE_ARRAY); + ASSERT_EQ(v.GetDoubleArray(), llvm::ArrayRef(vec)); + + // assign with different size + vec = {0.5,0.25}; + v.SetDoubleArray(vec); + ASSERT_EQ(ToNT(v).type, NT_DOUBLE_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_double.size, 2u); + ASSERT_EQ(ToNT(v).data.arr_double.arr[0], vec[0]); + ASSERT_EQ(ToNT(v).data.arr_double.arr[1], vec[1]); + ASSERT_EQ(v.type(), NT_DOUBLE_ARRAY); + ASSERT_EQ(v.GetDoubleArray(), llvm::ArrayRef(vec)); +} + +TEST_F(ValueTest, StringArray) { + Value v; + std::vector vec; + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodbye")); + vec.push_back(StringValue("string")); + v.SetStringArray(vec); + ASSERT_TRUE(vec.empty()); + ASSERT_EQ(ToNT(v).type, NT_STRING_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_string.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_string.arr[0].str, llvm::StringRef("hello")); + ASSERT_EQ(ToNT(v).data.arr_string.arr[1].str, llvm::StringRef("goodbye")); + ASSERT_EQ(ToNT(v).data.arr_string.arr[2].str, llvm::StringRef("string")); + ASSERT_EQ(v.type(), NT_STRING_ARRAY); + ASSERT_EQ(v.GetStringArray()[0], llvm::StringRef("hello")); + ASSERT_EQ(v.GetStringArray()[1], llvm::StringRef("goodbye")); + ASSERT_EQ(v.GetStringArray()[2], llvm::StringRef("string")); + + // assign with same size + vec.clear(); + vec.push_back(StringValue("s1")); + vec.push_back(StringValue("str2")); + vec.push_back(StringValue("string3")); + v.SetStringArray(vec); + ASSERT_TRUE(vec.empty()); + ASSERT_EQ(ToNT(v).type, NT_STRING_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_string.size, 3u); + ASSERT_EQ(ToNT(v).data.arr_string.arr[0].str, llvm::StringRef("s1")); + ASSERT_EQ(ToNT(v).data.arr_string.arr[1].str, llvm::StringRef("str2")); + ASSERT_EQ(ToNT(v).data.arr_string.arr[2].str, llvm::StringRef("string3")); + ASSERT_EQ(v.type(), NT_STRING_ARRAY); + ASSERT_EQ(v.GetStringArray()[0], llvm::StringRef("s1")); + ASSERT_EQ(v.GetStringArray()[1], llvm::StringRef("str2")); + ASSERT_EQ(v.GetStringArray()[2], llvm::StringRef("string3")); + + // assign with different size + vec.clear(); + vec.push_back(StringValue("short")); + vec.push_back(StringValue("er")); + v.SetStringArray(vec); + ASSERT_TRUE(vec.empty()); + ASSERT_EQ(ToNT(v).type, NT_STRING_ARRAY); + ASSERT_EQ(ToNT(v).data.arr_string.size, 2u); + ASSERT_EQ(ToNT(v).data.arr_string.arr[0].str, llvm::StringRef("short")); + ASSERT_EQ(ToNT(v).data.arr_string.arr[1].str, llvm::StringRef("er")); + ASSERT_EQ(v.type(), NT_STRING_ARRAY); + ASSERT_EQ(v.GetStringArray()[0], llvm::StringRef("short")); + ASSERT_EQ(v.GetStringArray()[1], llvm::StringRef("er")); +} + +TEST_F(ValueDeathTest, GetAssertions) { + Value v; + ASSERT_DEATH(v.GetBoolean(), "NT_Value::type == NT_BOOLEAN"); + ASSERT_DEATH(v.GetDouble(), "NT_Value::type == NT_DOUBLE"); + ASSERT_DEATH(v.GetString(), "NT_Value::type == NT_STRING"); + ASSERT_DEATH(v.GetRaw(), "NT_Value::type == NT_RAW"); + ASSERT_DEATH(v.GetBooleanArray(), "NT_Value::type == NT_BOOLEAN_ARRAY"); + ASSERT_DEATH(v.GetDoubleArray(), "NT_Value::type == NT_DOUBLE_ARRAY"); + ASSERT_DEATH(v.GetStringArray(), "NT_Value::type == NT_STRING_ARRAY"); +} + +TEST_F(ValueTest, UnassignedComparison) { + Value v1, v2; + ASSERT_EQ(v1, v2); +} + +TEST_F(ValueTest, MixedComparison) { + Value v1, v2; + v1.SetBoolean(true); + ASSERT_NE(v1, v2); // unassigned vs boolean + v2.SetDouble(0.5); + ASSERT_NE(v1, v2); // boolean vs double +} + +TEST_F(ValueTest, BooleanComparison) { + Value v1, v2; + v1.SetBoolean(true); + v2.SetBoolean(true); + ASSERT_EQ(v1, v2); + v2.SetBoolean(false); + ASSERT_NE(v1, v2); +} + +TEST_F(ValueTest, DoubleComparison) { + Value v1, v2; + v1.SetDouble(0.25); + v2.SetDouble(0.25); + ASSERT_EQ(v1, v2); + v2.SetDouble(0.5); + ASSERT_NE(v1, v2); +} + +TEST_F(ValueTest, StringComparison) { + Value v1, v2; + v1.SetString("hello"); + v2.SetString("hello"); + ASSERT_EQ(v1, v2); + v2.SetString("world"); // different contents + ASSERT_NE(v1, v2); + v2.SetString("goodbye"); // different size + ASSERT_NE(v1, v2); +} + +TEST_F(ValueTest, BooleanArrayComparison) { + Value v1, v2; + std::vector vec{1,0,1}; + v1.SetBooleanArray(vec); + v2.SetBooleanArray(vec); + ASSERT_EQ(v1, v2); + + // different contents + vec = {1,1,1}; + v2.SetBooleanArray(vec); + ASSERT_NE(v1, v2); + + // different size + vec = {1,0}; + v2.SetBooleanArray(vec); + ASSERT_NE(v1, v2); +} + +TEST_F(ValueTest, DoubleArrayComparison) { + Value v1, v2; + std::vector vec{0.5,0.25,0.5}; + v1.SetDoubleArray(vec); + v2.SetDoubleArray(vec); + ASSERT_EQ(v1, v2); + + // different contents + vec = {0.5,0.5,0.5}; + v2.SetDoubleArray(vec); + ASSERT_NE(v1, v2); + + // different size + vec = {0.5,0.25}; + v2.SetDoubleArray(vec); + ASSERT_NE(v1, v2); +} + +TEST_F(ValueTest, StringArrayComparison) { + Value v1, v2; + std::vector vec; + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodbye")); + vec.push_back(StringValue("string")); + v1.SetStringArray(vec); + vec.clear(); + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodbye")); + vec.push_back(StringValue("string")); + v2.SetStringArray(vec); + ASSERT_EQ(v1, v2); + + // different contents + vec.clear(); + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodby2")); + vec.push_back(StringValue("string")); + v2.SetStringArray(vec); + ASSERT_NE(v1, v2); + + // different sized contents + vec.clear(); + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodbye2")); + vec.push_back(StringValue("string")); + v2.SetStringArray(vec); + ASSERT_NE(v1, v2); + + // different size + vec.clear(); + vec.push_back(StringValue("hello")); + vec.push_back(StringValue("goodbye")); + v2.SetStringArray(vec); + ASSERT_NE(v1, v2); +} + +} // namespace ntimpl