// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed 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 "dragnn/runtime/extensions.h" #include #include #include #include "tensorflow/core/platform/test.h" namespace syntaxnet { namespace dragnn { namespace runtime { namespace { using ::testing::ElementsAre; // Dummy struct for tests. struct Foo { Foo() = default; explicit Foo(float real, int num) : real(real) { for (int i = 0; i < num; ++i) ints.push_back(i); } float real = 0.0; std::vector ints; }; // Returns a shared extension handle from the |manager|. template SharedExtensionHandle GetShared(ExtensionManager *manager) { SharedExtensionHandle handle; manager->GetShared(&handle); return handle; } // Returns a local extension handle from the |manager|. template LocalExtensionHandle AddLocal(ExtensionManager *manager) { LocalExtensionHandle handle; manager->AddLocal(&handle); return handle; } // Tests that GetShared() reuses existing extensions. TEST(ExtensionManagerTest, GetShared) { ExtensionManager manager; const auto foo_handle1 = GetShared(&manager); const auto int_handle = GetShared(&manager); const auto foo_handle2 = GetShared(&manager); Extensions extensions; extensions.Reset(&manager); Foo &foo1 = extensions.Get(foo_handle1); Foo &foo2 = extensions.Get(foo_handle2); EXPECT_EQ(&foo1, &foo2); EXPECT_EQ(foo1.real, 0.0); EXPECT_TRUE(foo1.ints.empty()); EXPECT_EQ(extensions.Get(int_handle), 0); // T() zero-initializes POD } // Tests that AddLocal() always adds a new extension. TEST(ExtensionManagerTest, AddLocal) { ExtensionManager manager; const auto foo_handle1 = AddLocal(&manager); const auto int_handle = AddLocal(&manager); const auto foo_handle2 = AddLocal(&manager); Extensions extensions; extensions.Reset(&manager); Foo &foo1 = extensions.Get(foo_handle1); Foo &foo2 = extensions.Get(foo_handle2); EXPECT_NE(&foo1, &foo2); EXPECT_EQ(foo1.real, 0.0); EXPECT_EQ(foo2.real, 0.0); EXPECT_TRUE(foo1.ints.empty()); EXPECT_TRUE(foo2.ints.empty()); EXPECT_EQ(extensions.Get(int_handle), 0); // T() zero-initializes POD } // Tests that Get() always returns the same object. TEST(ExtensionManagerTest, GetReturnsSameObject) { ExtensionManager manager; const auto foo_shared = GetShared(&manager); const auto int_shared = GetShared(&manager); const auto foo_local = AddLocal(&manager); const auto int_local = AddLocal(&manager); Extensions extensions; extensions.Reset(&manager); Foo &foo_shared1 = extensions.Get(foo_shared); int &int_shared1 = extensions.Get(int_shared); Foo &foo_local1 = extensions.Get(foo_local); int &int_local1 = extensions.Get(int_local); Foo &foo_shared2 = extensions.Get(foo_shared); int &int_shared2 = extensions.Get(int_shared); Foo &foo_local2 = extensions.Get(foo_local); int &int_local2 = extensions.Get(int_local); EXPECT_EQ(&foo_shared1, &foo_shared2); EXPECT_EQ(&int_shared1, &int_shared2); EXPECT_EQ(&foo_local1, &foo_local2); EXPECT_EQ(&int_local1, &int_local2); } // Tests that local extensions can use non-default constructors. TEST(ExtensionManagerTest, LocalAllowsNonDefaultConstructor) { ExtensionManager manager; const auto foo_handle = AddLocal(&manager); const auto int_handle = AddLocal(&manager); Extensions extensions; extensions.Reset(&manager); // Use non-default constructors to get initialized values. Foo &foo1 = extensions.Get(foo_handle, 0.5, 5); EXPECT_EQ(foo1.real, 0.5); EXPECT_THAT(foo1.ints, ElementsAre(0, 1, 2, 3, 4)); EXPECT_EQ(extensions.Get(int_handle, -123), -123); // However, once created, the non-default constructor args are ignored. Foo &foo2 = extensions.Get(foo_handle, 1.23, 1000); EXPECT_EQ(foo2.real, 0.5); EXPECT_THAT(foo2.ints, ElementsAre(0, 1, 2, 3, 4)); EXPECT_EQ(extensions.Get(int_handle, -456), -123); } // Tests that calling Reset() with the same manager is a NOP. TEST(ExtensionManagerTest, ResetWithSameManager) { ExtensionManager manager; const auto foo_shared = GetShared(&manager); const auto int_shared = GetShared(&manager); const auto foo_local = AddLocal(&manager); const auto int_local = AddLocal(&manager); Extensions extensions; extensions.Reset(&manager); Foo &foo_shared1 = extensions.Get(foo_shared); int &int_shared1 = extensions.Get(int_shared); Foo &foo_local1 = extensions.Get(foo_local); int &int_local1 = extensions.Get(int_local); extensions.Reset(&manager); Foo &foo_shared2 = extensions.Get(foo_shared); int &int_shared2 = extensions.Get(int_shared); Foo &foo_local2 = extensions.Get(foo_local); int &int_local2 = extensions.Get(int_local); EXPECT_EQ(&foo_shared1, &foo_shared2); EXPECT_EQ(&int_shared1, &int_shared2); EXPECT_EQ(&foo_local1, &foo_local2); EXPECT_EQ(&int_local1, &int_local2); } // Tests that Reset() can be used to switch managers. TEST(ExtensionManagerTest, ResetWithDifferentManager) { ExtensionManager manager1; const auto foo_shared = GetShared(&manager1); const auto foo_local = AddLocal(&manager1); ExtensionManager manager2; const auto int_shared = GetShared(&manager2); const auto int_local = AddLocal(&manager2); Extensions extensions; extensions.Reset(&manager1); EXPECT_EQ(extensions.Get(foo_shared).real, 0.0); EXPECT_EQ(extensions.Get(foo_local, 0.75, 3).real, 0.75); extensions.Reset(&manager2); EXPECT_EQ(extensions.Get(int_shared), 0); EXPECT_EQ(extensions.Get(int_local, 5), 5); } // Tests that Extensions supports move construction. TEST(ExtensionManagerTest, MoveConstruction) { ExtensionManager manager; const auto foo_shared = GetShared(&manager); const auto int_shared = GetShared(&manager); const auto foo_local = AddLocal(&manager); const auto int_local = AddLocal(&manager); // Add a couple more spurious extensions that are never set, to exercise // movement of non-present extensions. GetShared(&manager); AddLocal(&manager); Extensions extensions1; extensions1.Reset(&manager); Foo &foo_shared1 = extensions1.Get(foo_shared); int &int_shared1 = extensions1.Get(int_shared); Foo &foo_local1 = extensions1.Get(foo_local); int &int_local1 = extensions1.Get(int_local); Extensions extensions2 = std::move(extensions1); Foo &foo_shared2 = extensions2.Get(foo_shared); int &int_shared2 = extensions2.Get(int_shared); Foo &foo_local2 = extensions2.Get(foo_local); int &int_local2 = extensions2.Get(int_local); EXPECT_EQ(&foo_shared1, &foo_shared2); EXPECT_EQ(&int_shared1, &int_shared2); EXPECT_EQ(&foo_local1, &foo_local2); EXPECT_EQ(&int_local1, &int_local2); } // Tests that Extensions supports move assignment. TEST(ExtensionManagerTest, MoveAssignment) { ExtensionManager manager1; const auto foo_shared = GetShared(&manager1); const auto foo_local = AddLocal(&manager1); ExtensionManager manager2; const auto int_shared = GetShared(&manager2); const auto int_local = AddLocal(&manager2); // Add a couple more spurious extensions that are never set, to exercise // movement of non-present extensions. GetShared(&manager1); GetShared(&manager2); AddLocal(&manager1); AddLocal(&manager2); // Fill two sets of extensions. Extensions extensions1; extensions1.Reset(&manager1); extensions1.Get(foo_shared).real = 1.0; extensions1.Get(foo_local).real = 1.0; Extensions extensions2; extensions2.Reset(&manager2); extensions2.Get(int_shared) = 2; extensions2.Get(int_local) = 2; // Use a third set of extensions to perform a swap. Extensions extensions3; extensions3 = std::move(extensions1); extensions1 = std::move(extensions2); extensions2 = std::move(extensions3); EXPECT_EQ(extensions1.Get(int_shared), 2); EXPECT_EQ(extensions1.Get(int_local), 2); EXPECT_EQ(extensions2.Get(foo_shared).real, 1.0); EXPECT_EQ(extensions2.Get(foo_local).real, 1.0); } } // namespace } // namespace runtime } // namespace dragnn } // namespace syntaxnet