blob: 6e6acaafe42682b158c3bc879c87fee5338bfa46 [file]
/*
* 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 <gtest/gtest.h>
#include <tvm/ffi/container/dict.h>
#include <tvm/ffi/container/map.h>
#include <tvm/ffi/function.h>
namespace {
using namespace tvm::ffi;
TEST(Dict, Basic) {
Dict<String, int> d;
d.Set("a", 1);
d.Set("b", 2);
EXPECT_EQ(d.size(), 2);
EXPECT_EQ(d.at("a"), 1);
EXPECT_EQ(d["b"], 2);
EXPECT_EQ(d.count("a"), 1);
EXPECT_EQ(d.count("c"), 0);
EXPECT_FALSE(d.empty());
}
TEST(Dict, FindAndGet) {
Dict<String, int> d;
d.Set("x", 42);
auto it = d.find("x");
EXPECT_TRUE(it != d.end());
EXPECT_EQ((*it).second, 42);
auto it2 = d.find("y");
EXPECT_TRUE(it2 == d.end());
auto opt = d.Get("x");
ASSERT_TRUE(opt.has_value());
EXPECT_EQ(opt.value(), 42); // NOLINT(bugprone-unchecked-optional-access)
auto opt2 = d.Get("y");
EXPECT_FALSE(opt2.has_value());
}
TEST(Dict, SharedMutation) {
// Two handles point to the same DictObj
Dict<String, int> d1;
d1.Set("a", 1);
Dict<String, int> d2 = d1; // shallow copy of ObjectRef
// Mutate through d1
d1.Set("b", 2);
// d2 should see the change (no COW)
EXPECT_EQ(d2.size(), 2);
EXPECT_EQ(d2["b"], 2);
// Same underlying object
EXPECT_EQ(d1.get(), d2.get());
}
TEST(Dict, InplaceSwitchTo) {
// Insert >4 elements to trigger transition from small to dense layout.
// Verify the ObjectPtr address stays the same.
Dict<String, int> d1;
d1.Set("a", 1);
Dict<String, int> d2 = d1; // alias
const void* original_ptr = d1.get();
// Insert enough elements to trigger rehash
d1.Set("b", 2);
d1.Set("c", 3);
d1.Set("d", 4);
d1.Set("e", 5);
d1.Set("f", 6);
// ObjectPtr must be stable (InplaceSwitchTo)
EXPECT_EQ(static_cast<const void*>(d1.get()), original_ptr);
// Alias must point to same object and see all elements
EXPECT_EQ(static_cast<const void*>(d2.get()), original_ptr);
EXPECT_EQ(d2.size(), 6);
EXPECT_EQ(d2["f"], 6);
}
TEST(Dict, ManyElements) {
Dict<int, int> d;
for (int i = 0; i < 100; ++i) {
d.Set(i, i * 10);
}
EXPECT_EQ(d.size(), 100);
for (int i = 0; i < 100; ++i) {
EXPECT_EQ(d[i], i * 10);
}
}
TEST(Dict, Erase) {
Dict<String, int> d;
d.Set("a", 1);
d.Set("b", 2);
d.Set("c", 3);
d.erase("b");
EXPECT_EQ(d.size(), 2);
EXPECT_EQ(d.count("b"), 0);
EXPECT_EQ(d["a"], 1);
EXPECT_EQ(d["c"], 3);
}
TEST(Dict, Clear) {
Dict<String, int> d;
d.Set("a", 1);
d.Set("b", 2);
d.clear();
EXPECT_EQ(d.size(), 0);
EXPECT_TRUE(d.empty());
}
TEST(Dict, Iteration) {
Dict<String, int> d;
d.Set("x", 10);
d.Set("y", 20);
int count = 0;
for (auto [k, v] : d) {
++count;
if (k == "x") {
EXPECT_EQ(v, 10);
}
if (k == "y") {
EXPECT_EQ(v, 20);
}
}
EXPECT_EQ(count, 2);
}
TEST(Dict, PODKeys) {
Dict<int, String> d;
d.Set(1, "one");
d.Set(2, "two");
EXPECT_EQ(d[1], "one");
EXPECT_EQ(d[2], "two");
}
TEST(Dict, AnyConversion) {
Dict<Any, Any> d;
d.Set(String("key"), 42);
Any any_d = d;
auto d2 = any_d.cast<Dict<Any, Any>>();
EXPECT_EQ(d2.size(), 1);
}
TEST(Dict, InitializerList) {
Dict<String, int> d{{"a", 1}, {"b", 2}};
EXPECT_EQ(d.size(), 2);
EXPECT_EQ(d["a"], 1);
EXPECT_EQ(d["b"], 2);
}
TEST(Dict, UpdateExistingKey) {
Dict<String, int> d;
d.Set("a", 1);
d.Set("a", 2);
EXPECT_EQ(d.size(), 1);
EXPECT_EQ(d["a"], 2);
}
TEST(Dict, DefaultConstruction) {
Dict<String, int> d;
EXPECT_EQ(d.size(), 0);
EXPECT_TRUE(d.empty());
// Set on default-constructed should work
d.Set("a", 1);
EXPECT_EQ(d.size(), 1);
}
TEST(Dict, CrossConvMapToDict) {
Map<String, int> m{{"a", 1}, {"b", 2}};
Any any_m = m;
// Cast Map to Dict via Any — triggers cross-conversion
auto d = any_m.cast<Dict<String, int>>();
EXPECT_EQ(d.size(), 2);
EXPECT_EQ(d["a"], 1);
EXPECT_EQ(d["b"], 2);
}
TEST(Dict, CrossConvDictToMap) {
Dict<String, int> d{{"x", 10}, {"y", 20}};
Any any_d = d;
// Cast Dict to Map via Any — triggers cross-conversion
auto m = any_d.cast<Map<String, int>>();
EXPECT_EQ(m.size(), 2);
EXPECT_EQ(m["x"], 10);
EXPECT_EQ(m["y"], 20);
}
TEST(Dict, CrossConvEmptyMapToDict) {
Map<String, int> m;
Any any_m = m;
auto d = any_m.cast<Dict<String, int>>();
EXPECT_EQ(d.size(), 0);
EXPECT_TRUE(d.empty());
}
TEST(Dict, CrossConvEmptyDictToMap) {
Dict<String, int> d;
Any any_d = d;
auto m = any_d.cast<Map<String, int>>();
EXPECT_EQ(m.size(), 0);
EXPECT_TRUE(m.empty());
}
} // namespace