Addition of a new helper class to deal with WDL maps.
diff --git a/src/lib/profiles/data-management/Current/WdmDictionary.h b/src/lib/profiles/data-management/Current/WdmDictionary.h
new file mode 100644
index 0000000..5685465
--- /dev/null
+++ b/src/lib/profiles/data-management/Current/WdmDictionary.h
@@ -0,0 +1,230 @@
+/*
+ *
+ *    Copyright (c) 2018 Google LLC.
+ *    Copyright (c) 2013-2017 Nest Labs, 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.
+ */
+
+/**
+ *    @file
+ *      This file provides a more logical container representation of a WDM dictionary
+ *      that permits both indexing against the dictionary key as well as a more logical primary
+ *      key
+ */
+
+#ifndef _WEAVE_DATA_MANAGEMENT_WDM_DICTIONARY_H
+#define _WEAVE_DATA_MANAGEMENT_WDM_DICTIONARY_H
+
+#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
+
+#include <Weave/Core/WeaveCore.h>
+#include <Weave/Core/WeaveMessageLayer.h>
+#include <Weave/Profiles/ProfileCommon.h>
+#include <Weave/Support/CodeUtils.h>
+#include <Weave/Profiles/data-management/MessageDef.h>
+
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+
+using namespace boost::multi_index;
+
+namespace nl {
+namespace Weave {
+namespace Profiles {
+namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
+
+//
+// This templated class provides a more user-friendly type when interacting with WDL maps.
+// WDL maps are constrained in their keys to having 16-bit numerical keys. As such, these maps
+// cannot be thought of as traditional maps that have the key being the logical/primary key used to 
+// index the data in the collection. Rather, the key is merely a unique number assigned to each item,
+// lacking any semantic or logical meaning. The actual logical key is embedded in the item itself, and
+// be any WDL type. In this sense, WDL maps can be thought of as spare arrays.
+//
+// Applications will typically want to interact with the collection locally on devices more naturally as a keyed collection, 
+// with the data index using the logical key. This requires them to maintain a two-map solution: 
+// one to store the original data in the trait, and another to translate from the logical key to the map key.
+//
+// This templated class utilizes boost's multi_index_container, which permits indexing the data against multiple keys
+// while still retaining efficient storage and look-up of the data. The class wraps the provided boost type and
+// provides helper methods to interact with each key's sub-table. 
+//
+// Specializing the template requires passing in both the logical key type as well as the value type as well.
+//
+// Requirements on the types include:
+//      Logical Key Type: Has both '<' and '==' operators implemented.
+//      Value Type: Has the '==' operator implemented.
+//
+// The class also provides methods to compare against another instance of the collection to easily figure out the
+// set of items added, removed or modified against the logical key. This allows for comparisons
+//
+template <class KeyT, class ValueT>
+class WdmDictionary {
+public:
+    //
+    // This is the Item that is used in the collection, housing both the 16-bit dictionary
+    // key as well as the logical KeyT typed key.
+    // It also contains the value as well.
+    //
+    struct Item {
+        Item(uint16_t dictKey) { _dict_key = dictKey; _logical_key = 0; }
+        Item() { _dict_key = 0; _logical_key = 0; }
+
+        bool operator<(const Item& i1) const {
+            return (_logical_key < i1._logical_key);
+        }
+
+        bool operator ==(const Item& i) const {
+            return (_logical_key == i._logical_key && _dict_key == i._dict_key && _data == i._data);
+        }
+
+        ValueT _data;
+        uint16_t _dict_key;
+        KeyT _logical_key;
+    };
+
+    struct map_key{};
+    struct logical_key{};
+
+    typedef multi_index_container<
+        Item,
+        indexed_by<
+            // Both indexes are of type 'ordered_unique' since it's expected there will and should
+            // not be collisions in either of the key-spaces.
+            ordered_unique<tag<map_key>, member<Item, uint16_t, &Item::_dict_key> >,
+            ordered_unique<tag<logical_key>, member<Item, KeyT, &Item::_logical_key> >
+        >
+    > Container;
+
+    typedef typename Container::template index<map_key>::type DictKeyTableType;
+    typedef typename Container::template index<map_key>::type::iterator DictKeyTableTypeIterator;
+    typedef typename Container::template index<logical_key>::type LogicalKeyTableType;
+    typedef typename Container::template index<logical_key>::type::iterator LogicalKeyTableTypeIterator;
+
+    DictKeyTableType& GetDictKeyTable() { return _container.template get<map_key>(); }
+    LogicalKeyTableType& GetLogicalKeyTable() {  return _container.template get<logical_key>(); }
+
+    // 
+    // This is a convenience method that automatically creates an element in the collection with a particular
+    // dictionary key, or accesses an existing element that already exists, and allows the caller to mutate a member
+    // through a provided closure.
+    //
+    void ModifyItem(uint16_t dictKey, std::function<void(Item&)> func);
+   
+    //
+    // The following methods compare against a provided container and enumerates items that have been added, deleted or modified.
+    // This is specifically done against the logical key table and *not* against the dictionary keys. This is necessary since certain
+    // implementations of dictionaries (like the Nest cloud service) do not provide key stability for the dictionary keys, occasionally
+    // re-key'ing them while retaining the logical key + value content.
+    //
+    void ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
+    void ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
+    void ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func, bool updateStore=false);
+    bool IsEqual(WdmDictionary &dictionary);
+
+private: 
+    Container _container;
+};
+
+template <typename KeyT, typename ValueT>
+bool WdmDictionary<KeyT, ValueT>::IsEqual(WdmDictionary &dictionary)
+{
+    return (dictionary._container.size() == _container.size()) 
+        && std::equal(_container.begin(), _container.end(), dictionary._container.begin());
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ModifyItem(uint16_t dictKey, std::function<void(Item &)> func)
+{
+    Item item = Item(dictKey);
+    DictKeyTableType& mapkey_tbl = _container.template get<map_key>();
+    mapkey_tbl.modify(mapkey_tbl.insert(item).first, [&func](Item &d) {
+        func(d);
+    });
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore)
+{
+    Container addedItems;
+    LogicalKeyTableType &addedItemsLogicalTbl = addedItems.template get<logical_key>();
+
+    std::set_difference(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        std::inserter(addedItemsLogicalTbl, addedItemsLogicalTbl.begin()));
+
+    for (auto it = addedItemsLogicalTbl.begin(); it != addedItemsLogicalTbl.end(); it++) {
+        func(it);
+
+        if (updateStore) {
+            GetLogicalKeyTable().insert(*it);
+        }
+    }
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func, bool updateStore)
+{
+    Container removedItems;
+    LogicalKeyTableType &removedItemsLogicalTbl = removedItems.template get<logical_key>();
+
+    std::set_difference(GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        std::inserter(removedItemsLogicalTbl, removedItemsLogicalTbl.begin()));
+
+    for (auto it = removedItemsLogicalTbl.begin(); it != removedItemsLogicalTbl.end(); it++) {
+        func(it);
+
+        if (updateStore) {
+            GetLogicalKeyTable().erase(it->_logical_key);
+        }
+    }
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func, bool updateStore)
+{
+    Container modifiedItems;
+    LogicalKeyTableType &modifiedItemsTbl = modifiedItems.template get<logical_key>();
+
+    std::set_intersection(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        std::inserter(modifiedItemsTbl, modifiedItemsTbl.begin()));
+
+    for (auto it = modifiedItemsTbl.begin(); it != modifiedItemsTbl.end(); it++) {
+        auto it1 = GetLogicalKeyTable().find(it->_logical_key);
+        auto it2 = stagedContainer.GetLogicalKeyTable().find(it->_logical_key);
+
+        if (!(it1->_data == it2->_data)) {
+            func(it1, it2);
+        }
+
+        if (updateStore) {
+            GetLogicalKeyTable().modify(it1, [it2](auto &d) {
+                d = *it2;
+            });
+        }
+    }
+}
+
+}; // namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
+}; // namespace Profiles
+}; // namespace Weave
+}; // namespace nl
+
+#endif
diff --git a/src/test-apps/Makefile.am b/src/test-apps/Makefile.am
index 801e077..f1a7719 100644
--- a/src/test-apps/Makefile.am
+++ b/src/test-apps/Makefile.am
@@ -356,11 +356,13 @@
 if HAVE_CXX11
 check_PROGRAMS +=                                \
     TestTDM                                      \
+    TestTDMDictionary                            \
     $(NULL)
 endif
 
 check_PROGRAMS +=                                \
     TestTDM                                      \
+	TestTDMDictionary							 \
     TestPathStore                                \
     TestWdmUpdateEncoder                         \
     TestWdmUpdateResponse                        \
@@ -469,6 +471,7 @@
 if HAVE_CXX11
 local_test_programs                           += \
     TestTDM                                      \
+    TestTDMDictionary                            \
     TestWDM                                      \
     $(NULL)
 endif
@@ -1220,6 +1223,20 @@
 TestTDM_CPPFLAGS                         = $(AM_CPPFLAGS) -I$(top_srcdir)/src/test-apps/schema
 TestTDM_LDFLAGS                          = $(AM_CPPFLAGS)
 TestTDM_LDADD                            = libWeaveTestCommon.a $(COMMON_LDADD)
+
+TestTDMDictionary_SOURCES                = TestTDMDictionary.cpp \
+                                           schema/nest/test/trait/TestHTrait.cpp \
+                                           schema/nest/test/trait/TestCTrait.cpp \
+                                           schema/nest/test/trait/TestBTrait.cpp \
+                                           schema/nest/test/trait/TestMismatchedCTrait.cpp \
+                                           schema/nest/test/trait/TestCommon.cpp \
+                                           MockMismatchedSchemaSinkAndSource.cpp \
+                                           TestPersistedStorageImplementation.cpp \
+                                           MockTestBTrait.cpp
+
+TestTDMDictionary_CPPFLAGS              = $(AM_CPPFLAGS) -I$(top_srcdir)/src/test-apps/schema
+TestTDMDictionary_LDFLAGS               = $(AM_CPPFLAGS)
+TestTDMDictionary_LDADD                 = libWeaveTestCommon.a $(COMMON_LDADD)
 endif
 
 TestWDM_SOURCES                          = TestWdm.cpp
diff --git a/src/test-apps/TestTDM.cpp b/src/test-apps/TestTDM.cpp
index fd00d05..0170916 100644
--- a/src/test-apps/TestTDM.cpp
+++ b/src/test-apps/TestTDM.cpp
@@ -65,6 +65,116 @@
 using namespace nl::Weave::TLV;
 using namespace nl::Weave::Profiles::DataManagement;
 
+#include <boost/multi_index_container.hpp>
+#include <boost/multi_index/ordered_index.hpp>
+#include <boost/multi_index/identity.hpp>
+#include <boost/multi_index/member.hpp>
+#include <boost/multi_index/sequenced_index.hpp>
+
+using namespace boost::multi_index;
+
+template <class KeyT, class ValueT>
+class WdmDictionary {
+public:
+    struct Item {
+        Item(uint16_t dictKey) { _dict_key = dictKey; }
+
+        bool operator<(const Item& i1) const {
+            return (_logical_key < i1._logical_key);
+        }
+
+        ValueT _data;
+        uint16_t _dict_key;
+        KeyT _logical_key;
+    };
+
+    struct map_key{};
+    struct logical_key{};
+
+    typedef multi_index_container<
+        Item,
+        indexed_by<
+            ordered_unique<tag<map_key>, member<Item, uint16_t, &Item::_dict_key> >,
+            ordered_unique<tag<logical_key>, member<Item, KeyT, &Item::_logical_key> >
+        >
+    > Container;
+
+    typedef typename Container::template index<map_key>::type DictKeyTableType;
+    typedef typename Container::template index<logical_key>::type LogicalKeyTableType;
+    typedef typename Container::template index<logical_key>::type::iterator LogicalKeyTableTypeIterator;
+
+    void ModifyItem(uint16_t dictKey, std::function<void(Item&)> func);
+    DictKeyTableType& GetDictKeyTable() { return _container.template get<map_key>(); }
+    LogicalKeyTableType& GetLogicalKeyTable() {  return _container.template get<logical_key>(); }
+
+    void ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func);
+    void ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func);
+    void ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func);
+
+private: 
+    Container _container;
+};
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ModifyItem(uint16_t dictKey, std::function<void(Item &)> func)
+{
+    Item item = Item(dictKey);
+    DictKeyTableType& mapkey_tbl = _container.template get<map_key>();
+    mapkey_tbl.modify(mapkey_tbl.insert(item).first, [&func](Item &d) {
+        func(d);
+    });
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsAdded(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func)
+{
+    Container addedItems;
+    LogicalKeyTableType &addedItemsLogicalTbl = addedItems.template get<logical_key>();
+
+    std::set_difference(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        std::inserter(addedItemsLogicalTbl, addedItemsLogicalTbl.begin()));
+
+    for (auto it = addedItemsLogicalTbl.begin(); it != addedItemsLogicalTbl.end(); it++) {
+        func(it);
+    }
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsRemoved(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&)> func)
+{
+    Container removedItems;
+    LogicalKeyTableType &removedItemsLogicalTbl = removedItems.template get<logical_key>();
+
+    std::set_difference(GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        std::inserter(removedItemsLogicalTbl, removedItemsLogicalTbl.begin()));
+
+    for (auto it = removedItemsLogicalTbl.begin(); it != removedItemsLogicalTbl.end(); it++) {
+        func(it);
+    }
+}
+
+template <typename KeyT, typename ValueT>
+void WdmDictionary<KeyT, ValueT>::ItemsModified(WdmDictionary& stagedContainer, std::function<void(LogicalKeyTableTypeIterator&, LogicalKeyTableTypeIterator&)> func)
+{
+    Container modifiedItems;
+    LogicalKeyTableType &modifiedItemsTbl = modifiedItems.template get<logical_key>();
+
+    std::set_intersection(stagedContainer.GetLogicalKeyTable().begin(), stagedContainer.GetLogicalKeyTable().end(), 
+                        GetLogicalKeyTable().begin(), GetLogicalKeyTable().end(),
+                        std::inserter(modifiedItemsTbl, modifiedItemsTbl.begin()));
+
+    for (auto it = modifiedItemsTbl.begin(); it != modifiedItemsTbl.end(); it++) {
+        auto it1 = GetLogicalKeyTable().find(it->_logical_key);
+        auto it2 = stagedContainer.GetLogicalKeyTable().find(it->_logical_key);
+
+        if (!(it1->_data == it2->_data)) {
+            func(it1, it2);
+        }
+    }
+}
+
 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 //
 // System/Platform definitions
diff --git a/src/test-apps/TestTDMDictionary.cpp b/src/test-apps/TestTDMDictionary.cpp
new file mode 100644
index 0000000..ccf0e85
--- /dev/null
+++ b/src/test-apps/TestTDMDictionary.cpp
@@ -0,0 +1,765 @@
+/*
+ *
+ *    Copyright (c) 2018 Google LLC.
+ *    Copyright (c) 2013-2017 Nest Labs, 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.
+ */
+
+/**
+ *    @file
+ *      This file implements unit tests for the Weave TLV implementation.
+ *
+ */
+
+#include "ToolCommon.h"
+
+#include <nlbyteorder.h>
+#include <nlunit-test.h>
+
+#include <Weave/Core/WeaveCore.h>
+#include <Weave/Core/WeaveTLV.h>
+#include <Weave/Core/WeaveTLVDebug.hpp>
+#include <Weave/Core/WeaveTLVUtilities.hpp>
+#include <Weave/Core/WeaveTLVData.hpp>
+#include <Weave/Core/WeaveCircularTLVBuffer.h>
+#include <Weave/Support/RandUtils.h>
+
+#include <Weave/Profiles/data-management/Current/WdmManagedNamespace.h>
+#include <Weave/Profiles/data-management/DataManagement.h>
+
+#include <Weave/Profiles/data-management/WdmDictionary.h>
+
+#include <nest/test/trait/TestHTrait.h>
+#include <nest/test/trait/TestCTrait.h>
+#include <nest/test/trait/TestMismatchedCTrait.h>
+
+#include "MockMismatchedSchemaSinkAndSource.h"
+
+#include "MockTestBTrait.h"
+
+#include "MockPlatformClocks.h"
+
+#include <new>
+#include <map>
+#include <set>
+#include <algorithm>
+#include <set>
+#include <string>
+#include <iterator>
+
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+#include <lwip/init.h>
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
+
+using namespace nl;
+using namespace nl::Weave::TLV;
+using namespace nl::Weave::Profiles::DataManagement;
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// System/Platform definitions
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+namespace Private {
+
+System::Error SetClock_RealTime(uint64_t newCurTime)
+{
+    return WEAVE_SYSTEM_NO_ERROR;
+}
+
+static System::Error GetClock_RealTime(uint64_t & curTime)
+{
+    curTime = 0x42; // arbitrary non-zero value.
+    return WEAVE_SYSTEM_NO_ERROR;
+}
+
+} // namespace Private
+
+
+namespace nl {
+namespace Weave {
+namespace Profiles {
+namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
+namespace Platform {
+    // for unit tests, the dummy critical section is sufficient.
+    void CriticalSectionEnter()
+    {
+        return;
+    }
+
+    void CriticalSectionExit()
+    {
+        return;
+    }
+} // Platform
+} // WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
+}
+}
+}
+
+static SubscriptionEngine *gSubscriptionEngine;
+
+SubscriptionEngine * SubscriptionEngine::GetInstance()
+{
+    return gSubscriptionEngine;
+}
+
+static void TestTdmStatic_SingleLeafHandle(nlTestSuite *inSuite, void *inContext);
+
+// Test Suite
+
+/**
+ *  Test Suite that lists all the test functions.
+ */
+static const nlTest sTests[] = {
+    // Tests the static schema portions of TDM
+    NL_TEST_DEF("Test Tdm (Static schema): Single leaf handle", TestTdmStatic_SingleLeafHandle),
+    NL_TEST_SENTINEL()
+};
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+//
+// Testing NotificationEngine + TraitData
+//
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+
+namespace nl {
+namespace Weave {
+namespace Profiles {
+namespace WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current) {
+
+using namespace Schema::Nest::Test::Trait;
+
+class TestTdmSource : public TraitDataSource {
+public:
+    TestTdmSource();
+    void Reset();
+
+private:
+    WEAVE_ERROR GetLeafData(PropertyPathHandle aLeafHandle, uint64_t aTagToWrite, TLVWriter &aWriter);
+    WEAVE_ERROR GetNextDictionaryItemKey(PropertyPathHandle aDictionaryHandle, uintptr_t &aContext, PropertyDictionaryKey &aKey);
+
+public:
+    // 
+    // The logical key here is the field 'da', which is of type uint32.
+    //
+    WdmDictionary<uint32_t, TestHTrait::StructDictionary> _dict;
+};
+
+TestTdmSource::TestTdmSource()
+    : TraitDataSource(&TestHTrait::TraitSchema)
+{
+    // Using the Modify method to insert and modify.
+    _dict.ModifyItem(1, [](auto &item) {
+        item._logical_key = 10;
+        item._data.da = 10;
+        item._data.db = 1;
+        item._data.dc = 2;
+    });
+
+    _dict.ModifyItem(2, [](auto &item) {
+        item._logical_key = 20;
+        item._data.da = 20;
+        item._data.db = 3;
+        item._data.dc = 4;
+    });
+}
+
+void TestTdmSource::Reset()
+{
+}
+
+WEAVE_ERROR TestTdmSource::GetNextDictionaryItemKey(PropertyPathHandle aDictionaryHandle, uintptr_t &aContext, PropertyDictionaryKey &aKey)
+{
+    static WdmDictionary<uint32_t, TestHTrait::StructDictionary>::DictKeyTableTypeIterator it;
+
+    if (aContext == 0) {
+        it = _dict.GetDictKeyTable().begin();
+    }
+    else {
+        it++;
+    }
+
+    aContext = (uintptr_t)&it;
+
+    if (it == _dict.GetDictKeyTable().end()) {
+        return WEAVE_END_OF_INPUT;
+    }
+    else {
+        aKey = it->_dict_key;
+    }
+
+    return WEAVE_NO_ERROR;
+}
+
+WEAVE_ERROR TestTdmSource::GetLeafData(PropertyPathHandle aLeafHandle, uint64_t aTagToWrite, TLVWriter &aWriter)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    PropertyPathHandle dictionaryItemHandle = kNullPropertyPathHandle;
+
+    if (GetSchemaEngine()->IsInDictionary(aLeafHandle, dictionaryItemHandle)) {
+        PropertyPathHandle dictionaryHandle = GetSchemaEngine()->GetParent(dictionaryItemHandle);
+        PropertyDictionaryKey key = GetPropertyDictionaryKey(dictionaryItemHandle);
+
+        if (dictionaryHandle == TestHTrait::kPropertyHandle_L) {
+            auto it = _dict.GetDictKeyTable().find(key);
+
+            if (it != _dict.GetDictKeyTable().end()) {
+                TestHTrait::StructDictionary item = it->_data;
+                uint32_t val;
+
+                switch (GetPropertySchemaHandle(aLeafHandle)) {
+                    case TestHTrait::kPropertyHandle_L_Value_Da:
+                        WeaveLogDetail(DataManagement, "[TestTdmSource::GetLeafData] >> l[%u].da = %u", key, item.da);
+                        val = item.da;
+                        break;
+
+                    case TestHTrait::kPropertyHandle_L_Value_Db:
+                        WeaveLogDetail(DataManagement, "[TestTdmSource::GetLeafData] >> l[%u].db = %u", key, item.da);
+                        val = item.db;
+                        break;
+
+                    case TestHTrait::kPropertyHandle_L_Value_Dc:
+                        WeaveLogDetail(DataManagement, "[TestTdmSource::GetLeafData] >> l[%u].dc = %u", key, item.da);
+                        val = item.dc;
+                        break;
+
+                    default:
+                        WeaveLogError(DataManagement, "Unknown handle passed in!");
+                        return WEAVE_ERROR_INVALID_ARGUMENT;
+                        break;
+                }
+
+                err = aWriter.Put(aTagToWrite, val);
+                SuccessOrExit(err);
+            }
+            else {
+                WeaveLogError(DataManagement, "Requested key %u for dictionary handle %u that doesn't exist!", key, dictionaryHandle);
+                return WEAVE_ERROR_INVALID_ARGUMENT;
+            }
+        }
+    }
+
+exit:
+    return err;
+}
+
+
+class TestTdmSink : public TraitDataSink {
+public:
+    TestTdmSink();
+    void Reset();
+    void DumpChangeSets();
+
+private:
+    WEAVE_ERROR OnEvent(uint16_t aType, void *aInParam);
+    WEAVE_ERROR SetLeafData(PropertyPathHandle aLeafHandle, nl::Weave::TLV::TLVReader &aReader);
+
+public:
+    //
+    // Main canonical store of truth
+    //
+    WdmDictionary<uint32_t, TestHTrait::StructDictionary> _dict;
+
+    //
+    // Staged changes that are accrued and applied at the end
+    // of a logical change
+    //
+    WdmDictionary<uint32_t, TestHTrait::StructDictionary> _stagedDict;
+
+    //
+    // Staged set of deleted path handles
+    //
+    std::set<PropertyPathHandle> _deletedDictItems;
+
+    //
+    // Staged item that gets appended to the staged dictionary on completion
+    // of the modification.
+    WdmDictionary<uint32_t, TestHTrait::StructDictionary>::Item _stagedDictItem;
+
+    bool _isReplaceOperation;
+};
+
+TestTdmSink::TestTdmSink()
+    : TraitDataSink(&TestHTrait::TraitSchema)
+{
+}
+
+void TestTdmSink::Reset()
+{
+    ClearVersion();
+    _stagedDict.GetDictKeyTable().clear();
+    _deletedDictItems.clear();
+}
+
+WEAVE_ERROR TestTdmSink::OnEvent(uint16_t aType, void *aInParam)
+{
+    InEventParam *inParam = static_cast<InEventParam *>(aInParam);
+
+    switch (aType) {
+        case kEventChangeBegin:
+            _stagedDict.GetDictKeyTable().clear();
+            _deletedDictItems.clear();
+            _isReplaceOperation = false;
+            break;
+
+        case kEventDictionaryItemDelete:
+            WeaveLogError(DataManagement, "[TestTdmSink::OnEvent] Deleting %u:%u", 
+                          GetPropertyDictionaryKey(inParam->mDictionaryItemDelete.mTargetHandle), GetPropertySchemaHandle(inParam->mDictionaryItemDelete.mTargetHandle));
+
+
+            if (GetPropertySchemaHandle(inParam->mDictionaryItemDelete.mTargetHandle) == TestHTrait::kPropertyHandle_L) {
+                _deletedDictItems.insert(GetPropertyDictionaryKey(inParam->mDictionaryItemDelete.mTargetHandle));
+            }
+
+            break;
+
+        case kEventDictionaryItemModifyBegin:
+        {
+            auto it = _dict.GetDictKeyTable().find(GetPropertyDictionaryKey(inParam->mDictionaryItemModifyBegin.mTargetHandle));
+
+            WeaveLogError(DataManagement, "[TestTdmSink::OnEvent] Adding/Modifying %u:%u", GetPropertyDictionaryKey(inParam->mDictionaryItemModifyBegin.mTargetHandle), GetPropertySchemaHandle(inParam->mDictionaryItemModifyBegin.mTargetHandle));
+
+            _stagedDictItem._dict_key = GetPropertyDictionaryKey(inParam->mDictionaryItemModifyBegin.mTargetHandle);
+            if (it != _dict.GetDictKeyTable().end()) {
+                _stagedDictItem = *it;
+            }
+
+            break;
+        }
+
+        case kEventDictionaryItemModifyEnd:
+            _stagedDict.GetDictKeyTable().insert(_stagedDictItem);
+            break;
+
+        case kEventDictionaryReplaceBegin:
+            WeaveLogError(DataManagement, "[TestTdmSink::OnEvent] Replacing %u:%u", GetPropertyDictionaryKey(inParam->mDictionaryReplaceBegin.mTargetHandle), GetPropertySchemaHandle(inParam->mDictionaryReplaceBegin.mTargetHandle));
+
+            _isReplaceOperation = true;
+            _stagedDict.GetDictKeyTable().clear();
+            break;
+
+
+        case kEventChangeEnd:
+            WeaveLogError(DataManagement, "[TestTdmSink::OnEvent] Change End");
+           
+            //
+            // Delete items
+            // 
+            for (auto it = _deletedDictItems.begin(); it != _deletedDictItems.end(); it++) {
+                _dict.GetDictKeyTable().erase(*it);
+            }
+
+            _dict.ItemsAdded(_stagedDict, [](auto &i) {
+                printf("A %u: %u\n", i->_logical_key, i->_data.db);
+            }, true);
+
+            //
+            // Only do the negative intersection if we're doing a full replace on the dictionary. Otherwise, we'll
+            // unintentionally remove elements.
+            //
+            if (_isReplaceOperation) {
+                _dict.ItemsRemoved(_stagedDict, [](auto &i) {
+                    printf("R %u: %u\n", i->_logical_key, i->_data.db);
+                }, true);
+            }
+
+            _dict.ItemsModified(_stagedDict, [](auto &oldd, auto &newd) {
+                printf("M %u: %u %u\n", oldd->_logical_key, oldd->_data.db, newd->_data.db);
+            }, true);
+
+            break;
+    }
+
+    return WEAVE_NO_ERROR;
+}
+
+WEAVE_ERROR TestTdmSink::SetLeafData(PropertyPathHandle aHandle, TLVReader &aReader)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    PropertySchemaHandle schemaHandle = GetPropertySchemaHandle(aHandle);
+
+    WeaveLogError(DataManagement, "[TestTdmSink::SetLeafData] << %u:%u", GetPropertyDictionaryKey(aHandle), GetPropertySchemaHandle(aHandle));
+    
+    switch (schemaHandle) {
+        case TestHTrait::kPropertyHandle_L_Value_Da:
+        {
+            err = aReader.Get(_stagedDictItem._data.da);
+            SuccessOrExit(err);
+
+            _stagedDictItem._logical_key = _stagedDictItem._data.da;
+            break;
+        }
+
+        case TestHTrait::kPropertyHandle_L_Value_Db:
+        {
+            err = aReader.Get(_stagedDictItem._data.db);
+            SuccessOrExit(err);
+            break;
+        }
+
+        case TestHTrait::kPropertyHandle_L_Value_Dc:
+        {
+            err = aReader.Get(_stagedDictItem._data.dc);
+            SuccessOrExit(err);
+            break;
+        }
+    }
+
+exit:
+    return err;
+}
+
+class TestTdm {
+public:
+    TestTdm();
+
+    int Setup();
+    int Teardown();
+    int Reset();
+    int BuildAndProcessNotify();
+
+    void TestTdmStatic_SingleLeafHandle(nlTestSuite *inSuite);
+
+private:
+    SubscriptionHandler *mSubHandler;
+    SubscriptionClient *mSubClient;
+    NotificationEngine *mNotificationEngine;
+
+    SubscriptionEngine mSubscriptionEngine;
+    WeaveExchangeManager mExchangeMgr;
+    SingleResourceSourceTraitCatalog::CatalogItem mSourceCatalogStore[5];
+    SingleResourceSourceTraitCatalog mSourceCatalog;
+    SingleResourceSinkTraitCatalog::CatalogItem mSinkCatalogStore[5];
+    SingleResourceSinkTraitCatalog mSinkCatalog;
+    TestTdmSource mTestTdmSource;
+    TestTdmSink mTestTdmSink;
+    Binding *mClientBinding;
+    uint32_t mTestCase;
+    WEAVE_ERROR AllocateBuffer(uint32_t desiredSize, uint32_t minSize);
+};
+
+TestTdm::TestTdm()
+    : mSourceCatalog(ResourceIdentifier(ResourceIdentifier::SELF_NODE_ID), mSourceCatalogStore, 5),
+      mSinkCatalog(ResourceIdentifier(ResourceIdentifier::SELF_NODE_ID), mSinkCatalogStore, 5),
+      mClientBinding(NULL)
+{
+    mTestCase = 0;
+}
+
+int TestTdm::Setup()
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    TraitDataHandle testTdmSourceHandle;
+    TraitDataHandle testTdmSinkHandle;
+    SubscriptionHandler::TraitInstanceInfo *traitInstance = NULL;
+
+    gSubscriptionEngine = &mSubscriptionEngine;
+
+    // Initialize SubEngine and set it up
+    err = mSubscriptionEngine.Init(&ExchangeMgr, NULL, NULL);
+    SuccessOrExit(err);
+
+    err = mSubscriptionEngine.EnablePublisher(NULL, &mSourceCatalog);
+    SuccessOrExit(err);
+
+    // Get a sub handler and prime it to the right state
+    err = mSubscriptionEngine.NewSubscriptionHandler(&mSubHandler);
+    SuccessOrExit(err);
+
+    mSubHandler->mBinding = ExchangeMgr.NewBinding();
+    mSubHandler->mBinding->BeginConfiguration().Transport_UDP();
+
+    mClientBinding = ExchangeMgr.NewBinding();
+
+    err = mSubscriptionEngine.NewClient(&mSubClient, mClientBinding, NULL, NULL, &mSinkCatalog, 0);
+    SuccessOrExit(err);
+
+    mNotificationEngine = &mSubscriptionEngine.mNotificationEngine;
+
+    mSourceCatalog.Add(0, &mTestTdmSource, testTdmSourceHandle);
+    mSinkCatalog.Add(0, &mTestTdmSink, testTdmSinkHandle);
+
+    traitInstance = mSubscriptionEngine.mTraitInfoPool;
+    mSubHandler->mTraitInstanceList = traitInstance;
+    mSubHandler->mNumTraitInstances++;
+    ++(SubscriptionEngine::GetInstance()->mNumTraitInfosInPool);
+
+    traitInstance->Init();
+    traitInstance->mTraitDataHandle = testTdmSourceHandle;
+    traitInstance->mRequestedVersion = 1;
+
+exit:
+    if (err != WEAVE_NO_ERROR) {
+        WeaveLogError(DataManagement, "Error setting up test: %d", err);
+    }
+
+    return err;
+}
+
+void TestTdm::TestTdmStatic_SingleLeafHandle(nlTestSuite *inSuite)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    bool testPass = false;
+
+    Reset();
+
+    //
+    // Replace
+    //
+    mTestTdmSource.Lock(); 
+    mTestTdmSource.SetDirty(TestHTrait::kPropertyHandle_L);
+    mTestTdmSource.Unlock();
+
+    err = BuildAndProcessNotify();
+    SuccessOrExit(err);
+
+    printf("Equal: %d\n", mTestTdmSource._dict.IsEqual(mTestTdmSink._dict));
+
+    //
+    // Delete Item
+    //
+    mTestTdmSource.Lock(); 
+    mTestTdmSource._dict.GetDictKeyTable().erase(0); 
+    mTestTdmSource.DeleteKey(CreatePropertyPathHandle(TestHTrait::kPropertyHandle_L_Value, 0));
+    mTestTdmSource.Unlock();
+
+    err = BuildAndProcessNotify();
+    SuccessOrExit(err);
+
+    printf("Equal: %d\n", mTestTdmSource._dict.IsEqual(mTestTdmSink._dict));
+   
+    //
+    // Add Item
+    //
+    mTestTdmSource.Lock();
+    mTestTdmSource._dict.ModifyItem(10, [](auto &i) {
+        i._logical_key = 300;
+        i._data.da = 300;
+        i._data.db = 30;
+        i._data.dc = 30;
+    });
+
+    mTestTdmSource.Lock(); 
+    mTestTdmSource.SetDirty(CreatePropertyPathHandle(TestHTrait::kPropertyHandle_L_Value, 10));
+    mTestTdmSource.Unlock();
+
+    err = BuildAndProcessNotify();
+    SuccessOrExit(err);
+
+    printf("Equal: %d\n", mTestTdmSource._dict.IsEqual(mTestTdmSink._dict));
+
+    //
+    // Change dictionary keys, but keep logical keys + data stable.
+    //
+    mTestTdmSource.Lock();
+    mTestTdmSource._dict.ModifyItem(10, [](auto &i) {
+        i._dict_key = 100;
+    });
+
+    mTestTdmSource._dict.ModifyItem(1, [](auto &i) {
+        i._dict_key = 1000;
+    });
+    
+    mTestTdmSource.SetDirty(TestHTrait::kPropertyHandle_L);
+    mTestTdmSource.Unlock();
+
+    err = BuildAndProcessNotify();
+    SuccessOrExit(err);
+
+    printf("Equal: %d\n", mTestTdmSource._dict.IsEqual(mTestTdmSink._dict));
+    
+    
+exit:
+    NL_TEST_ASSERT(inSuite, testPass);
+}
+
+int TestTdm::Teardown()
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    if (mClientBinding != NULL)
+    {
+        mClientBinding->Release();
+        mClientBinding = NULL;
+    }
+
+    return err;
+}
+
+int TestTdm::Reset()
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+
+    mSubHandler->MoveToState(SubscriptionHandler::kState_SubscriptionEstablished_Idle);
+    
+    mTestTdmSink.Reset();
+    mTestTdmSource.Reset();
+
+    mNotificationEngine->mGraphSolver.ClearDirty();
+
+    return err;
+}
+
+int TestTdm::BuildAndProcessNotify()
+{
+    bool isSubscriptionClean;
+    NotificationEngine::NotifyRequestBuilder notifyRequest;
+    NotificationRequest::Parser notify;
+    PacketBuffer *buf = NULL;
+    TLVWriter writer;
+    TLVReader reader;
+    TLVType dummyType1, dummyType2;
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    bool neWriteInProgress = false;
+    uint32_t maxNotificationSize = 0;
+    uint32_t maxPayloadSize = 0;
+
+    maxNotificationSize = mSubHandler->GetMaxNotificationSize();
+
+    err = mSubHandler->mBinding->AllocateRightSizedBuffer(buf, maxNotificationSize, WDM_MIN_NOTIFICATION_SIZE, maxPayloadSize);
+    SuccessOrExit(err);
+
+    err = notifyRequest.Init(buf, &writer, mSubHandler, maxPayloadSize);
+    SuccessOrExit(err);
+
+    err = mNotificationEngine->BuildSingleNotifyRequestDataList(mSubHandler, notifyRequest, isSubscriptionClean, neWriteInProgress);
+    SuccessOrExit(err);
+
+    if (neWriteInProgress)
+    {
+        err = notifyRequest.MoveToState(NotificationEngine::kNotifyRequestBuilder_Idle);
+        SuccessOrExit(err);
+
+        reader.Init(buf);
+
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        notify.Init(reader);
+
+        err = notify.CheckSchemaValidity();
+        SuccessOrExit(err);
+
+        // Enter the struct
+        err = reader.EnterContainer(dummyType1);
+        SuccessOrExit(err);
+
+        // SubscriptionId
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        err = reader.Next();
+        SuccessOrExit(err);
+
+        VerifyOrExit(nl::Weave::TLV::kTLVType_Array == reader.GetType(), err = WEAVE_ERROR_WRONG_TLV_TYPE);
+
+        err = reader.EnterContainer(dummyType2);
+        SuccessOrExit(err);
+
+        err = mSubClient->ProcessDataList(reader);
+        SuccessOrExit(err);
+    }
+    else
+    {
+        WeaveLogDetail(DataManagement, "nothing has been written");
+    }
+
+exit:
+    if (buf) {
+        PacketBuffer::Free(buf);
+    }
+
+    return err;
+}
+
+WEAVE_ERROR TestTdm::AllocateBuffer(uint32_t desiredSize, uint32_t minSize)
+{
+    WEAVE_ERROR err = WEAVE_NO_ERROR;
+    uint32_t maxPayloadSize = 0;
+    PacketBuffer *buf = NULL;
+
+
+    err = mSubHandler->mBinding->AllocateRightSizedBuffer(buf, desiredSize, minSize, maxPayloadSize);
+    SuccessOrExit(err);
+
+exit:
+
+    if (buf)
+    {
+        PacketBuffer::Free(buf);
+    }
+
+    return err;
+}
+
+} // WeaveMakeManagedNamespaceIdentifier(DataManagement, kWeaveManagedNamespaceDesignation_Current)
+}
+}
+}
+
+TestTdm *gTestTdm;
+
+/**
+ *  Set up the test suite.
+ */
+static int TestSetup(void *inContext)
+{
+    static TestTdm testTdm;
+    gTestTdm = &testTdm;
+
+    return testTdm.Setup();
+}
+
+/**
+ *  Tear down the test suite.
+ */
+static int TestTeardown(void *inContext)
+{
+    return gTestTdm->Teardown();
+}
+
+static void TestTdmStatic_SingleLeafHandle(nlTestSuite *inSuite, void *inContext)
+{
+    gTestTdm->TestTdmStatic_SingleLeafHandle(inSuite);
+}
+
+/**
+ *  Main
+ */
+int main(int argc, char *argv[])
+{
+#if WEAVE_SYSTEM_CONFIG_USE_LWIP
+    tcpip_init(NULL, NULL);
+#endif // WEAVE_SYSTEM_CONFIG_USE_LWIP
+
+    MockPlatform::gMockPlatformClocks.GetClock_RealTime = Private::GetClock_RealTime;
+    MockPlatform::gMockPlatformClocks.SetClock_RealTime = Private::SetClock_RealTime;
+
+    nlTestSuite theSuite = {
+        "weave-tdm",
+        &sTests[0],
+        TestSetup,
+        TestTeardown
+    };
+
+    // Generate machine-readable, comma-separated value (CSV) output.
+    nl_test_set_output_style(OUTPUT_CSV);
+
+    // Run test suit against one context
+    nlTestRunner(&theSuite, NULL);
+
+    return nlTestRunnerStats(&theSuite);
+}
diff --git a/src/test-apps/schema/nest/test/trait/TestHTrait.h b/src/test-apps/schema/nest/test/trait/TestHTrait.h
index c3badd4..d7a1d75 100644
--- a/src/test-apps/schema/nest/test/trait/TestHTrait.h
+++ b/src/test-apps/schema/nest/test/trait/TestHTrait.h
@@ -186,6 +186,10 @@
     uint32_t db;
     uint32_t dc;
 
+    bool operator ==(const StructDictionary& a) const {
+        return (memcmp(&a, this, sizeof(StructDictionary)) == 0);
+    }
+
     static const nl::SchemaFieldDescriptor FieldSchema;
 
 };