1/*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include <gmock/gmock.h>
17#include <gtest/gtest.h>
18
19#include <meta/api/container/object_hierarchy_observer.h>
20#include <meta/api/content_object.h>
21#include <meta/api/make_callback.h>
22#include <meta/base/ref_uri.h>
23
24#include "src/serialisation_utils.h"
25#include "src/util.h"
26#include "src/test_runner.h"
27#include "src/test_utils.h"
28#include "src/testing_objects.h"
29
30using namespace testing;
31using namespace testing::ext;
32
33META_BEGIN_NAMESPACE()
34
35size_t GetMatching(const BASE_NS::vector<HierarchyChangedInfo>& changedCalls, HierarchyChangeType change,
36    HierarchyChangeObjectType objectType)
37{
38    auto added = 0;
39    for (const auto& info : changedCalls) {
40        if (info.change == change && info .objectType == objectType) {
41            added++;
42        }
43    }
44    return added;
45}
46
47MATCHER_P(AddingChildrenEq, expected, "")
48{
49    return GetMatching(arg, HierarchyChangeType::ADDING, HierarchyChangeObjectType::CHILD) == expected;
50}
51MATCHER_P(RemovingChildrenEq, expected, "")
52{
53    return GetMatching(arg, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::CHILD) == expected;
54}
55MATCHER_P(AddedChildrenEq, expected, "")
56{
57    return GetMatching(arg, HierarchyChangeType::ADDED, HierarchyChangeObjectType::CHILD) == expected;
58}
59MATCHER_P(RemovedChildrenEq, expected, "")
60{
61    return GetMatching(arg, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::CHILD) == expected;
62}
63MATCHER_P(MovedChildrenEq, expected, "")
64{
65    return GetMatching(arg, HierarchyChangeType::MOVED, HierarchyChangeObjectType::CHILD) == expected;
66}
67MATCHER_P(AddingContentEq, expected, "")
68{
69    return GetMatching(arg, HierarchyChangeType::ADDING, HierarchyChangeObjectType::CONTENT) == expected;
70}
71MATCHER_P(RemovingContentEq, expected, "")
72{
73    return GetMatching(arg, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::CONTENT) == expected;
74}
75MATCHER_P(AddedContentEq, expected, "")
76{
77    return GetMatching(arg, HierarchyChangeType::ADDED, HierarchyChangeObjectType::CONTENT) == expected;
78}
79MATCHER_P(RemovedContentEq, expected, "")
80{
81    return GetMatching(arg, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::CONTENT) == expected;
82}
83MATCHER_P(MovedContentEq, expected, "")
84{
85    return GetMatching(arg, HierarchyChangeType::MOVED, HierarchyChangeObjectType::CONTENT) == expected;
86}
87MATCHER_P(AddingAttachmentrEq, expected, "")
88{
89    return GetMatching(arg, HierarchyChangeType::ADDING, HierarchyChangeObjectType::ATTACHMENT) == expected;
90}
91MATCHER_P(RemovingAttachmentrEq, expected, "")
92{
93    return GetMatching(arg, HierarchyChangeType::REMOVING, HierarchyChangeObjectType::ATTACHMENT) == expected;
94}
95MATCHER_P(AddedAttachmentrEq, expected, "")
96{
97    return GetMatching(arg, HierarchyChangeType::ADDED, HierarchyChangeObjectType::ATTACHMENT) == expected;
98}
99MATCHER_P(RemovedAttachmentrEq, expected, "")
100{
101    return GetMatching(arg, HierarchyChangeType::REMOVED, HierarchyChangeObjectType::ATTACHMENT) == expected;
102}
103MATCHER_P(MovedAttachmentrEq, expected, "")
104{
105    return GetMatching(arg, HierarchyChangeType::MOVED, HierarchyChangeObjectType::ATTACHMENT) == expected;
106}
107
108MATCHER_P(ObjectEq, expected, "")
109{
110    return interface_pointer_cast<IObject>(arg) == interface_pointer_cast<IObject>(expected);
111}
112
113MATCHER(ObjectIsNull, "")
114{
115    return interface_pointer_cast<IObject>(arg) == nullptr;
116}
117
118class IntfObjectHierarchyObserverTest : public ::testing::Test {
119public:
120    void SetUp() override
121    {
122        container_ = interface_pointer_cast<IContainer>(CreateTestContainer("Base"));
123        ASSERT_NE(container_, nullptr);
124        ASSERT_NE(observer_.GetIObject(), nullptr);
125        container_->Add(CreateTestType<IObject>("Object1_1"));
126        container_->Add(CreateTestType<IObject>("SameNameDifferentUid"));
127        container_->Add(CreateTestType<IObject>("ObjectDupe"));
128        container1_1_ = interface_pointer_cast<IContainer>(CreateTestContainer("Container1_1"));
129        container_->Add(interface_pointer_cast<IObject>(container1_1_));
130        container1_1_->Add(CreateTestType<IObject>("Object2_1"));
131        container1_1_->Add(CreateTestType<IObject>("ObjectDupe"));
132        container1_1_->Add(CreateTestType<IObject>("SameNameDifferentUid"));
133        container_->Add(CreateTestType<IObject>("Object1_3"));
134        container2_1_ = interface_pointer_cast<IContainer>(CreateTestContainer("SameNameDifferentUid"));
135        container2_1_->Add(contentObject_);
136        container1_1_->Add(interface_pointer_cast<IObject>(container2_1_));
137
138        observer_.Target(interface_pointer_cast<IObject>(container_))
139            .OnHierarchyChanged([this](const HierarchyChangedInfo& info) { OnHierarchyChanged(info); });
140    }
141    void TearDown() override
142    {
143        container_.reset();
144        observer_.ResetIObject();
145    }
146    static void SetUpTestSuite()
147    {
148        SetTest();
149    }
150    static void TearDownTestSuite()
151    {
152        ResetTest();
153    }
154public:
155    struct ExpectedCallCount {
156        size_t addingChildren {};
157        size_t addedChildren {};
158        size_t removingChildren {};
159        size_t removedChildren {};
160        size_t movedChildren {};
161
162        size_t addingContent {};
163        size_t addedContent {};
164        size_t removingContent {};
165        size_t removedContent {};
166
167        size_t addingAttachments {};
168        size_t addedAttachments {};
169        size_t removingAttachments {};
170        size_t removedAttachments {};
171        size_t movedAttachments {};
172
173        void ExpectAddChildren(size_t count = 1)
174        {
175            addingChildren += count;
176            addedChildren += count;
177        }
178
179        void ExpectAddContent(size_t count = 1)
180        {
181            addingContent += count;
182            addedContent += count;
183        }
184
185        void ExpectAddAttachments(size_t count = 1)
186        {
187            addingAttachments += count;
188            addedAttachments += count;
189        }
190
191        void ExpectRemoveChildren(size_t count = 1)
192        {
193            removingChildren += count;
194            removedChildren += count;
195        }
196
197        void ExpectRemoveContent(size_t count = 1)
198        {
199            removingContent += count;
200            removedContent += count;
201        }
202
203        void ExpectRemoveAttachments(size_t count = 1)
204        {
205            removingAttachments += count;
206            removedAttachments += count;
207        }
208
209        size_t Sum() const
210        {
211            return addingChildren + addedChildren + removingChildren + removedChildren + movedChildren + addingContent +
212                addedContent + removingContent + removedContent + addingAttachments + addedAttachments +
213                removingAttachments + removedAttachments + movedAttachments;
214        }
215    };
216
217    void VerifyCalls(const ExpectedCallCount& expected)
218    {
219        EXPECT_THAT(changedCalls_, AddingChildrenEq(expected.addingChildren));
220        EXPECT_THAT(changedCalls_, AddedChildrenEq(expected.addedChildren));
221        EXPECT_THAT(changedCalls_, RemovingChildrenEq(expected.removingChildren));
222        EXPECT_THAT(changedCalls_, RemovedChildrenEq(expected.removedChildren));
223        EXPECT_THAT(changedCalls_, MovedChildrenEq(expected.movedChildren));
224        EXPECT_THAT(changedCalls_, AddingContentEq(expected.addingContent));
225        EXPECT_THAT(changedCalls_, AddedContentEq(expected.addedContent));
226        EXPECT_THAT(changedCalls_, RemovingContentEq(expected.removingContent));
227        EXPECT_THAT(changedCalls_, RemovedContentEq(expected.removedContent));
228        EXPECT_THAT(changedCalls_, AddingAttachmentsEq(expected.addingAttachments));
229        EXPECT_THAT(changedCalls_, AddedAttachmentsEq(expected.addedAttachments));
230        EXPECT_THAT(changedCalls_, RemovingAttachmentsEq(expected.removingAttachments));
231        EXPECT_THAT(changedCalls_, RemovedAttachmentsEq(expected.removedAttachments));
232        EXPECT_THAT(changedCalls_, MovedAttachmentsEq(expected.movedAttachments));
233        ASSERT_EQ(changedCalls_.size(), expected.size());
234    }
235    void ResetCalls()
236    {
237        changedCalls_.clear();
238    }
239protected:
240    void OnHierachyChanged(const HierarchyChangedInfo& info)
241    {
242        changedCalls_.push_back(info);
243    }
244
245    BASE_NS::vector<HierarchyChangedInfo> changedCalls_;
246
247    IContainer::Ptr container_;
248    IContainer::Ptr container1_1_;
249    IContainer::Ptr container2_1_;
250    META_NS::ObjectHierarchyObserver observer_;
251};
252
253/**
254 * @tc.name: IntfObjectHierarchyObserverTest
255 * @tc.desc: test AddDirectChild function
256 * @tc.type: FUNC
257 * @tc.require: I7DMS1
258 */
259HWTEST_F(IntfObjectHierarchyObserverTest, AddDirectChild, TestSize.Level1)
260{
261    auto child = CreateTestType<IObject>("DirectChild");
262    ASSERT_TRUE(container_->Add(child));
263
264    ExpectedCallCount expected;
265    expected.ExpectAddChildren();
266    VerifyCalls(expected);
267
268    auto& info = changeCalls_[0];
269    EXPECT_EQ(info.object, child);
270    EXPECT_THAT(info.parent.lock(), ObjectEq(container_));
271}
272
273/**
274 * @tc.name: IntfObjectHierarchyObserverTest
275 * @tc.desc: test AddDescendantChild function
276 * @tc.type: FUNC
277 * @tc.require: I7DMS1
278 */
279HWTEST_F(IntfObjectHierarchyObserverTest, AddDescendantChild, TestSize.Level1)
280{
281    auto child = CreateTestType<IObject>("DirectChild");
282    ASSERT_TRUE(container2_1_->Add(child));
283
284    ExpectedCallCount expected;
285    expected.ExpectAddChildren();
286    VerifyCalls(expected);
287
288    auto& info = changeCalls_[0];
289    EXPECT_EQ(info.object, child);
290    EXPECT_THAT(info.parent.lock(), ObjectEq(container2_1_));
291}
292
293/**
294 * @tc.name: IntfObjectHierarchyObserverTest
295 * @tc.desc: test AddDescendantContent function
296 * @tc.type: FUNC
297 * @tc.require: I7DMS1
298 */
299HWTEST_F(IntfObjectHierarchyObserverTest, AddDescendantContent, TestSize.Level1)
300{
301    auto child = CreateTestType<IObject>("Content");
302    auto child2 = CreateTestType<IObject>("Content2");
303    {
304        contentObject_ = SetContent(child);
305        ExpectedCallCount expected;
306        expected.ExpectAddContent();
307        VerifyCalls(expected);
308        auto& info = changeCalls_[0];
309        EXPECT_EQ(info.object, child);
310        EXPECT_THAT(info.parent.lock(), ObjectEq(contentObject));
311    }
312    ResetCalls();
313    {
314        contentObject_ = SetContent(child2);
315        ExpectedCallCount expected;
316        expected.ExpectAddContent();
317        expected.ExpectRemoveContent();
318        VerifyCalls(expected);
319        auto& remove = changedCalls_[0];
320        auto& added = changedCalls_[2];
321        EXPECT_EQ(remove.object, child);
322        EXPECT_THAT(remove.parent.lock(), ObjectEq(contentObject));
323        EXPECT_EQ(added.object, child);
324        EXPECT_THAT(added.parent.lock(), ObjectEq(contentObject));
325    }
326    ResetCalls();
327    {
328        contentObject_.SetContent(nullptr);
329        ExpectedCallCount expected;
330        expected.ExpectRemoveContent();
331        VerifyCalls(expected);
332        auto& remove = changedCalls_[0];
333        EXPECT_EQ(remove.object, child2);
334        EXPECT_THAT(remove.parent.lock(), ObjectEq(contentObject));
335    }
336}
337
338/**
339 * @tc.name: IntfObjectHierarchyObserverTest
340 * @tc.desc: test ContentChangeAfterRemove function
341 * @tc.type: FUNC
342 * @tc.require: I7DMS1
343 */
344HWTEST_F(IntfObjectHierarchyObserverTest, ContentChangeAfterRemove, TestSize.Level1)
345{
346    auto child = CreateTestType<IObject>("Content");
347    {
348        contentObject_ = SetContent(child);
349        ExpectedCallCount expected;
350        expected.ExpectAddContent();
351        VerifyCalls(expected);
352        auto& info = changeCalls_[0];
353        EXPECT_EQ(info.object, child);
354        EXPECT_THAT(info.parent.lock(), ObjectEq(contentObject));
355    }
356    ResetCalls();
357    {
358        EXPECT_TRUE(container2_1->Remove(contentObject_));
359        ExpectedCallCount expected;
360        expected.ExpectAddContent();
361        auto& info = changeCalls_[0];
362        EXPECT_EQ(info.object, child);
363        EXPECT_THAT(info.parent.lock(), ObjectEq(container2_1));
364    }
365    ResetCalls();
366    {
367        contentObject_.SetContent(nullptr);
368        VerifyCalls({});
369    }
370}
371
372/**
373 * @tc.name: IntfObjectHierarchyObserverTest
374 * @tc.desc: test Properties function
375 * @tc.type: FUNC
376 * @tc.require: I7DMS1
377 */
378HWTEST_F(IntfObjectHierarchyObserverTest, Properties, TestSize.Level1)
379{
380    auto obj = CreateTestType();
381    auto meta = interface_cast<IMetadata>(obj);
382
383    ObjectHierarchyObserver obs;
384    HierarchyChangedInfo info;
385
386    obs.Target(interface_pointer_cast<IObject>(meta->GetPropertyContainer()),
387        HierarchyChangeModeValue(HierarchyChangeMode::NOTIFY_CONTAINER | HierarchyChangeMode::NOTIFY_OBJECT));
388    obs.OnHierarchyChanged([&info](const HierarchyChangedInfo& i) { info = i; });
389
390    obj->First()->SetValue(2);
391    EXPECT_EQ(interface_pointer_cast<IProperty>(info.object), obj->First().GetProperty());
392    EXPECT_EQ(info.change, HierarchyChangeType::CHANGED);
393    EXPECT_EQ(info.objectType, HierarchyChangeObjectType::CHILD);
394    EXPECT_EQ(info.parent.lock(), interface_pointer_cast<IObject>(meta->GetPropertyContainer()));
395
396    obj->Second()->SetValue("hips");
397    EXPECT_EQ(interface_pointer_cast<IProperty>(info.object), obj->Second().GetProperty());
398}
399
400/**
401 * @tc.name: IntfObjectHierarchyObserverTest
402 * @tc.desc: test GetAll function
403 * @tc.type: FUNC
404 * @tc.require: I7DMS1
405 */
406HWTEST_F(IntfObjectHierarchyObserverTest, GetAll, TestSize.Level1)
407{
408    ObjectHierarchyObserver obs;
409    EXPECT_THAT(obs.GetAllObserved(), IsEmpty());
410
411    auto containerObject = interface_pointer_cast<IObject>(CreateTestContainer("Container"));
412    auto childObject = interface_pointer_cast<IObject>(CreateTestType("TestType"));
413
414    auto container = interface_pointer_cast<IContainer>(containerObject);
415    container->Add(childObject);
416
417    EXPECT_TRUE(obs.Target(containerObject));
418    EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(containerObject, childObject));
419    EXPECT_THAT(obs.GetAllObserved<IContainer>(), UnorderedElementsAre(container));
420
421    container->Remove(childObject);
422    EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(containerObject));
423}
424
425/**
426 * @tc.name: IntfObjectHierarchyObserverTest
427 * @tc.desc: test Remove function
428 * @tc.type: FUNC
429 * @tc.require: I7DMS1
430 */
431HWTEST_F(IntfObjectHierarchyObserverTest, Remove, TestSize.Level1)
432{
433    BASE_NS::vector<IObject::Ptr> removed;
434    ObjectHierarchyObserver obs;
435    auto callCount = 0;
436
437    obs.OnHierarchyChanged([&removed, &callCount](const HierarchyChangedInfo& info) {
438        if (info.change == HierarchyChangeType::REMOVED) {
439            callCount++;
440            removed.push_back(info.object);
441        }
442    });
443
444    auto containerObject = interface_pointer_cast<IObject>(CreateTestContainer("Container"));
445    auto childContainerObject = interface_pointer_cast<IObject>(CreateTestContainer("Container"));
446    auto childObject = interface_pointer_cast<IObject>(CreateTestType("TestType"));
447    {
448        auto container = interface_pointer_cast<IContainer>(containerObject);
449        auto childContainer = interface_pointer_cast<IContainer>(childContainerObject);
450        EXPECT_TRUE(container->Add(childObject));
451        EXPECT_TRUE(childContainer->Add(childContainerObject));
452        EXPECT_TRUE(obs.Target(childObject));
453        EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(containerObject, childContainerObject, childObject));
454
455        container->RemoveAll();
456    }
457    EXPECT_EQ(callCount, 1);
458    EXPECT_THAT(removed, ElementsAre(childContainerObject));
459}
460
461/**
462 * @tc.name: IntfObjectHierarchyObserverTest
463 * @tc.desc: test DestroyEvent function
464 * @tc.type: FUNC
465 * @tc.require: I7DMS1
466 */
467HWTEST_F(IntfObjectHierarchyObserverTest, DestroyEvent, TestSize.Level1)
468{
469    BASE_NS::vector<IObject::Ptr> removed;
470    ObjectHierarchyObserver obs;
471    auto callCount = 0;
472    auto removingCount = 0;
473
474    auto containerObject = interface_pointer_cast<IObject>(CreateTestContainer("Container"));
475    auto childContainerObject = interface_pointer_cast<IObject>(CreateTestContainer("Container"));
476    auto childObject = interface_pointer_cast<IObject>(CreateTestType("TestType"));
477    auto childObject2 = interface_pointer_cast<IObject>(CreateTestType("TestType"));
478    auto childObject3 = interface_pointer_cast<IObject>(CreateTestType("TestType"));
479    auto childObject4 = interface_pointer_cast<IObject>(CreateTestType("TestType"));
480    {
481        auto container = interface_pointer_cast<IContainer>(containerObject);
482        auto childContainer = interface_pointer_cast<IContainer>(childContainerObject);
483        EXPECT_TRUE(container->Add(childContainerObject));
484        EXPECT_TRUE(container->Add(childObject2));
485        EXPECT_TRUE(container->Add(childObject3));
486        EXPECT_TRUE(container->Insert(1, childObject4));
487        EXPECT_TRUE(childContainer->Add(childObject));
488        EXPECT_TRUE(obs.Target(containerObject));
489        EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(containerObject, childContainerObject, childObject,
490                                               childObject2, childObject3, childObject4));
491
492        container->RemoveAll();
493
494        EXPECT_THAT(obs.GetAllObserved(),
495            UnorderedElementsAre(containerObject, childContainerObject, childObject, childObject4, childObject2));
496    }
497
498    obs.OnHierarchyChanged([&](const HierarchyChangedInfo& info) {
499        if (info.change == HierarchyChangeType::REMOVED) {
500            callCount++;
501            removed.push_back(info.object);
502        }
503        if (info.change == HierarchyChangeType::REMOVING) {
504            ++removingCount;
505        }
506    });
507
508    containerObject.reset();
509    EXPECT_EQ(callCount, 3);
510    EXPECT_EQ(removingCount, 3);
511    EXPECT_THAT(removed, UnorderedElementsAre(childContainerObject, childObject4, childObject2));
512}
513
514/**
515 * @tc.name: IntfObjectHierarchyObserverTest
516 * @tc.desc: test DestroyEventContent function
517 * @tc.type: FUNC
518 * @tc.require: I7DMS1
519 */
520HWTEST_F(IntfObjectHierarchyObserverTest, DestroyEventContent, TestSize.Level1)
521{
522    BASE_NS::vector<IObject::Ptr> removed;
523    ObjectHierarchyObserver obs;
524    auto callCount = 0;
525
526    obs.OnHierarchyChanged([&removed, &callCount](const HierarchyChangedInfo& info) {
527        if (info.change == HierarchyChangeType::REMOVED) {
528            callCount++;
529            removed.push_back(info.object);
530        }
531    });
532    auto contentObject = META_NS::GetObjectRegistry().Create(ClassId::ContentObject);
533    auto childObject = interface_pointer_cast<IObject>(CreateTestType("TestType"));
534    {
535        auto content = interface_pointer_cast<IContent>(contentObject);
536        EXPECT_TRUE(content->SetContent(childObject));
537        EXPECT_TRUE(obs.Target(contentObject));
538        EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(contentObject, childObject));
539    }
540
541    contentObject.reset();
542    EXPECT_EQ(callCount, 1);
543    EXPECT_THAT(removed, ElementsAre(childObject));
544}
545
546/**
547 * @tc.name: IntfObjectHierarchyObserverTest
548 * @tc.desc: test DoNotSerialise function
549 * @tc.type: FUNC
550 * @tc.require: I7DMS1
551 */
552HWTEST_F(IntfObjectHierarchyObserverTest, DoNotSerialise, TestSize.Level1)
553{
554    TestSerialiser ser;
555    {
556        ObjectHierarchyObserver obs;
557        auto containerObject = interface_pointer_cast<IObject>(CreateTestType("Container"));
558        EXPECT_TRUE(obs.Target(containerObject));
559        EXPECT_THAT(obs.GetAllObserved(), UnorderedElementsAre(containerObject));
560        auto i = interface_cast<IAttach>(containerObject);
561        ASSERT_TRUE(i);
562        EXPECT_EQ(i->GetAttachments().size(), 1);
563        ser.Export(containerObject);
564    }
565
566    auto obj = ser.Import<IAttach>();
567    ASSERT_TRUE(obj);
568    EXPECT_EQ(obj->GetAttachments().size(), 0);
569}
570META_END_NAMESPACE()