[PyTorch] Additional IValue tests (#49718)
Summary:
Pull Request resolved: https://github.com/pytorch/pytorch/pull/49718
Improving test coverage in preparation for updating the
implementation of IValue.
ghstack-source-id: 119327373
Test Plan: ivalue_test
Reviewed By: hlu1
Differential Revision: D25674605
fbshipit-source-id: 37a82bb135f75ec52d2d8bd929c4329e8dcc4d25
diff --git a/aten/src/ATen/test/ivalue_test.cpp b/aten/src/ATen/test/ivalue_test.cpp
index 14e7520..a0e2648 100644
--- a/aten/src/ATen/test/ivalue_test.cpp
+++ b/aten/src/ATen/test/ivalue_test.cpp
@@ -51,6 +51,91 @@
ASSERT_EQ(tv.use_count(), 2);
}
+static std::array<IValue, 5> makeSampleIValues() {
+ return { at::rand({3, 4}), "hello", 42, true, 1.5 };
+}
+
+static std::array<IValue, 5> makeMoreSampleIValues() {
+ return { at::rand({3, 4}), "goodbye", 23, false, 0.5 };
+}
+
+// IValue::operator== doesn't seem to work on Tensors.
+#define EXPECT_IVALUE_EQ(a, b) \
+ EXPECT_EQ((a).isTensor(), (b).isTensor()); \
+ if ((a).isTensor()) { \
+ EXPECT_TRUE(a.toTensor().equal(b.toTensor())); \
+ } else { \
+ EXPECT_EQ(a, b); \
+ }
+
+TEST(IValueTest, Swap) {
+ // swap() has the following 3 cases: tensor, intrusive_ptr, or
+ // neither. Exercise all pairs of the three.
+
+ auto sampleInputs = makeSampleIValues();
+ auto sampleTargets = makeMoreSampleIValues();
+ for (const auto& input: sampleInputs) {
+ for (const auto& target: sampleTargets) {
+ IValue a(input);
+ IValue b(target);
+ EXPECT_IVALUE_EQ(a, input);
+ EXPECT_IVALUE_EQ(b, target);
+ a.swap(b);
+ EXPECT_IVALUE_EQ(a, target);
+ EXPECT_IVALUE_EQ(b, input);
+ }
+ }
+}
+
+TEST(IValueTest, CopyConstruct) {
+ auto sampleInputs = makeSampleIValues();
+ for (const IValue& v: sampleInputs) {
+ IValue copy(v);
+ EXPECT_IVALUE_EQ(copy, v);
+ }
+}
+
+TEST(IValueTest, MoveConstruct) {
+ auto sampleInputs = makeSampleIValues();
+ for (const IValue& v: sampleInputs) {
+ IValue source(v);
+ IValue target(std::move(source));
+ EXPECT_IVALUE_EQ(target, v);
+ EXPECT_TRUE(source.isNone());
+ }
+}
+
+TEST(IValueTest, CopyAssign) {
+ auto sampleInputs = makeSampleIValues();
+ auto sampleTargets = makeMoreSampleIValues();
+
+ for (const IValue& input: sampleInputs) {
+ for (const IValue& target: sampleTargets) {
+ IValue copyTo(target);
+ IValue copyFrom(input);
+ copyTo = copyFrom;
+ EXPECT_IVALUE_EQ(copyTo, input);
+ EXPECT_IVALUE_EQ(copyFrom, input);
+ EXPECT_IVALUE_EQ(copyTo, copyFrom);
+ }
+ }
+}
+
+TEST(IValueTest, MoveAssign) {
+ auto sampleInputs = makeSampleIValues();
+ auto sampleTargets = makeMoreSampleIValues();
+
+ for (const IValue& input: sampleInputs) {
+ for (const IValue& target: sampleTargets) {
+ IValue moveTo(target);
+ IValue moveFrom(input);
+ moveTo = std::move(moveFrom);
+ EXPECT_IVALUE_EQ(moveTo, input);
+ EXPECT_TRUE(moveFrom.isNone());
+ }
+ }
+}
+
TEST(IValueTest, Tuple) {
std::tuple<int64_t, at::Tensor> t = std::make_tuple(123, at::randn({1}));
auto iv = IValue(t);
@@ -318,5 +403,137 @@
);
}
+TEST(IValueTest, isPtrType) {
+ IValue tensor(at::rand({3, 4}));
+ IValue undefinedTensor((at::Tensor()));
+ IValue integer(42);
+ IValue str("hello");
+
+ EXPECT_TRUE(tensor.isPtrType());
+ EXPECT_FALSE(undefinedTensor.isPtrType());
+ EXPECT_FALSE(integer.isPtrType());
+ EXPECT_TRUE(str.isPtrType());
+}
+
+TEST(IValueTest, isAliasOf) {
+ auto sampleIValues = makeSampleIValues();
+ for (auto& iv: sampleIValues) {
+ for (auto& iv2: sampleIValues) {
+ if (&iv == &iv2 && iv.isPtrType()) {
+ EXPECT_TRUE(iv.isAliasOf(iv2));
+ } else {
+ EXPECT_FALSE(iv.isAliasOf(iv2));
+ }
+ }
+ }
+}
+
+TEST(IValueTest, internalToPointer) {
+ IValue tensor(at::rand({3, 4}));
+ IValue str("hello");
+
+ EXPECT_EQ(tensor.internalToPointer(), tensor.unsafeToTensorImpl());
+ EXPECT_NE(str.internalToPointer(), nullptr);
+
+ IValue nullStr((c10::intrusive_ptr<ivalue::ConstantString>()));
+ ASSERT_TRUE(nullStr.isString());
+ EXPECT_EQ(nullStr.internalToPointer(), nullptr);
+}
+
+TEST(IValueTest, IdentityComparisonAndHashing) {
+ at::Tensor t1 = at::rand({3, 4});
+ at::Tensor t2 = at::rand({3, 4});
+ IValue tv1(t1), tv2(t2);
+ IValue tv1b(t1);
+
+ EXPECT_EQ(tv1.hash(), tv1b.hash());
+ EXPECT_NE(tv1.hash(), tv2.hash());
+
+ EXPECT_TRUE(tv1.is(tv1));
+ EXPECT_TRUE(tv1.is(tv1b));
+ EXPECT_TRUE(tv1b.is(tv1));
+ EXPECT_TRUE(tv2.is(tv2));
+
+ EXPECT_FALSE(tv1.is(tv2));
+ EXPECT_FALSE(tv2.is(tv1));
+
+ IValue none;
+ IValue undefinedTensor((at::Tensor()));
+
+ EXPECT_TRUE(none.is(undefinedTensor));
+ EXPECT_TRUE(undefinedTensor.is(none));
+
+ // Is this a bug? We should probably have a is b => a.hash() == b.hash()
+ EXPECT_NE(none.hash(), undefinedTensor.hash());
+
+ auto sampleIValues = makeSampleIValues();
+ auto sampleIValues2 = makeSampleIValues();
+ auto moreSampleIValues = makeMoreSampleIValues();
+
+ ASSERT_EQ(sampleIValues.size(), moreSampleIValues.size());
+ for (int ii = 0; ii < sampleIValues.size(); ++ii) {
+ // Constant strings will have the same pointer value.
+ if (sampleIValues[ii].isPtrType() && !sampleIValues[ii].isString()) {
+ EXPECT_NE(sampleIValues[ii].hash(), sampleIValues2[ii].hash());
+ } else {
+ EXPECT_EQ(sampleIValues[ii].hash(), sampleIValues2[ii].hash());
+ }
+ EXPECT_NE(sampleIValues[ii].hash(), moreSampleIValues[ii].hash());
+ }
+}
+
+TEST(IValueTest, getSubValues) {
+ // Scalars have no subvalues.
+ IValue integer(42), float_(1.5);
+
+ IValue::HashAliasedIValues subvalues;
+
+ integer.getSubValues(subvalues);
+ EXPECT_TRUE(subvalues.empty());
+
+ subvalues.clear();
+
+ float_.getSubValues(subvalues);
+ EXPECT_TRUE(subvalues.empty());
+
+ subvalues.clear();
+
+ at::Tensor t1(at::rand({3, 4})), t2(at::rand({3, 4}));
+ IValue tv1(t1), tv2(t2);
+ IValue list(std::vector<at::Tensor>{t1, t2});
+ IValue tuple(ivalue::Tuple::create({tv1, tv2}));
+
+ std::unordered_map<int64_t, at::Tensor> m;
+ m[1] = t1;
+ m[2] = t2;
+
+ IValue dict(std::move(m));
+
+ auto objType = ClassType::create(nullopt, {});
+ objType->addAttribute("t1", tv1.type());
+ objType->addAttribute("t2", tv2.type());
+
+ auto o = ivalue::Object::create(StrongTypePtr(nullptr, objType), 2);
+ o->setSlot(0, tv1);
+ o->setSlot(1, tv2);
+
+ IValue object(o);
+ tv1.getSubValues(subvalues);
+ EXPECT_EQ(subvalues.size(), 1);
+ EXPECT_EQ(subvalues.count(tv1), 1);
+
+ subvalues.clear();
+
+ for (auto& container: {list, tuple, dict, object}) {
+ container.getSubValues(subvalues);
+ EXPECT_EQ(subvalues.size(), 3);
+ EXPECT_EQ(subvalues.count(container), 1);
+ EXPECT_EQ(subvalues.count(tv1), 1);
+ EXPECT_EQ(subvalues.count(tv2), 1);
+
+ subvalues.clear();
+ }
+}
+
// TODO(gmagogsfm): Add type conversion test?
} // namespace c10