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
This commit is contained in:
Peter Johnson
2015-07-03 13:29:31 -07:00
parent b66fb68f29
commit 5a0fccc9cf
8 changed files with 479 additions and 28 deletions

View File

@@ -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)

View File

@@ -40,24 +40,6 @@ void Value::SetBooleanArray(llvm::ArrayRef<int> value) {
std::copy(value.begin(), value.end(), data.arr_boolean.arr);
}
void Value::SetBooleanArray(llvm::ArrayRef<bool> 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<int*>(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<double> value) {
// handle type change
if (NT_Value::type != NT_DOUBLE_ARRAY) {
@@ -89,6 +71,11 @@ void Value::SetStringArray(std::vector<StringValue>& value) {
std::free(data.arr_string.arr);
data.arr_string.arr =
static_cast<NT_String*>(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<value.size(); ++i) {
data.arr_string.arr[i].str = nullptr;
data.arr_string.arr[i].len = 0;
}
data.arr_string.size = value.size();
}
std::move(value.begin(), value.end(),

View File

@@ -18,13 +18,16 @@
namespace ntimpl {
class StringValueTest;
class Storage;
class Value;
class ValueTest;
/*
* C++ wrapper class around NT_String.
*/
class StringValue : private NT_String {
friend class StringValueTest;
friend class Value;
public:
StringValue() { NT_InitString(this); }
@@ -59,6 +62,7 @@ class StringValue : private NT_String {
* C++ wrapper class around NT_Value.
*/
class Value : private NT_Value {
friend class ValueTest;
friend class Storage;
public:
Value() { NT_InitValue(this); }
@@ -86,8 +90,6 @@ class Value : private NT_Value {
assert(NT_Value::type == NT_RAW);
return static_cast<const StringValue&>(data.v_raw);
}
// Ideally this would return llvm::ArrayRef<bool> but the C headers must
// use "int" and casting may be very unsafe.
llvm::ArrayRef<int> GetBooleanArray() const {
assert(NT_Value::type == NT_BOOLEAN_ARRAY);
return llvm::ArrayRef<int>(data.arr_boolean.arr, data.arr_boolean.size);
@@ -97,7 +99,7 @@ class Value : private NT_Value {
return llvm::ArrayRef<double>(data.arr_double.arr, data.arr_double.size);
}
llvm::ArrayRef<StringValue> GetStringArray() const {
assert(NT_Value::type == NT_BOOLEAN_ARRAY);
assert(NT_Value::type == NT_STRING_ARRAY);
return llvm::ArrayRef<StringValue>(
static_cast<StringValue*>(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<StringValue&>(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<StringValue&>(data.v_raw) = std::move(value);
}
void SetBooleanArray(llvm::ArrayRef<int> value);
void SetBooleanArray(llvm::ArrayRef<bool> value);
void SetDoubleArray(llvm::ArrayRef<double> 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

1
test/CMakeLists.txt Normal file
View File

@@ -0,0 +1 @@
add_subdirectory(unit)

10
test/unit/CMakeLists.txt Normal file
View File

@@ -0,0 +1,10 @@
file(GLOB SRCS *.cpp)
add_executable(testntcore ${SRCS})
target_link_libraries(testntcore
ntcore
libgmock
libgtest
)

8
test/unit/main.cpp Normal file
View File

@@ -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;
}

View File

@@ -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

347
test/unit/test_Value.cpp Normal file
View File

@@ -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<int> 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<int>(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<int>(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<int>(vec));
}
TEST_F(ValueTest, DoubleArray) {
Value v;
std::vector<double> 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<double>(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<double>(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<double>(vec));
}
TEST_F(ValueTest, StringArray) {
Value v;
std::vector<StringValue> 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<int> 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<double> 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<StringValue> 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